1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Threading; 9 using Antmicro.Renode.Debugging; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Utilities; 12 13 namespace Antmicro.Renode.Time 14 { 15 /// <summary> 16 /// Handle used for synchronization and communication between <see cref="ITimeSource"> and <see cref="ITimeSink">. 17 /// </summary> 18 public class TimeHandle : IdentifiableObject 19 { 20 // ------- 21 // The objects of this class are used to synchronize execution of `time sources` and `time sinks`. 22 // 23 // [SOURCE SIDE] [SINK SIDE] 24 // Dispose 25 // | 26 // V 27 // +--------+ 28 // Latch -> | | 29 // ... | | 30 // (Grant / Unblock + (Latch)) -> | | <- Request* + (Latch) 31 // ... | Time | ... 32 // WaitUntilDone* + (Unlatch) -> | Handle | <- ReportBreak / ReportContinue 33 // ... | | 34 // Unlatch -> | | 35 // | | 36 // +--------+ 37 // --- properties --- 38 // +--------+ 39 // SourceSideActive = | | = SinkSideActive 40 // | | = Enabled 41 // +--------+ 42 // 43 // 44 // Methods marked with '*' are blocking: 45 // * `Request` will block until `Grant` or `Unblock` 46 // * `WaitUntilDone` will block until `ReportBreak` or `ReportContinue` 47 // 48 // Methods surrounded with '()' are executed conditionally: 49 // * `Latch` as a result of `Request` is executed only if this is the first `Request` after `ReportBreak` 50 // * `Unlatch` as a result of `WaitUntilDone` is executed only if this is the first `WaitUntilDone` after successful unblocking of the handle 51 // * `Grant` is not executed as long as the previous `WaitUntilDone` does not finish successfully, returning `true` 52 // * `Unlock` is executed only when the previous `WaitUntilDone` returned `false` 53 // 54 // 55 // SOURCE SIDE simplified algorithm: 56 // (1) `Latch` the handle 57 // (2?) `Grant` time or `Unblock` the handle if previous `WaitUntilDone` failed 58 // (3) Call `WaitUntilDone` 59 // (4) `Unlatch` the handle 60 // (5) Go to p. (1) 61 // 62 // SINK SIDE simplified algorithm: 63 // (1) 'Request' time 64 // (2) Execute the time-aware code for a given virtual time 65 // (2.1) Finish the execution when granted virtual time is depleted using `ReportContinue` 66 // (2.2) Stop the execution in an arbitrary moment with the intent of resuming in the future and use `ReportBreak` 67 // (3) Go to p. (1) 68 // 69 // 70 // Properties: 71 // * `SourceSideActive` - when set to `false`: `Request` returns immediately with the `false` result 72 // * `SinkSideActive` - when set to `false`: `WaitUntilDone` returns immediately with the `false` result 73 // * `Enabled` - when set to `false`: `WaitUntilDone` returns immediately with the `true` result 74 // * `DeferredEnabled` - `Enabled` will be assigned this value when unlatched 75 // 76 // Internal state: 77 // * `sourceSideInProgress` - `true` from `Grant` to `WaitUntilDone` or `Dispose` 78 // * `sinkSideInProgress` - `true` from `Request` to `ReportBreak` or `ReportContinue` or `Dispose` 79 // * `grantPending` - `true` from `Grant` or `Unblock` to `Request` 80 // * `reportPending` - `true` from `ReportBreak` or `ReportContinue` to `WaitUntilDone` 81 // * `isBlocking` - `true` from `ReportBreak` to `Request` 82 // 83 // Additional notes: 84 // 85 // 1. `Active` means that the code on source/sink side is working and follows the above-mentioned algorithm. 86 // Going `inactive` is a signal for a handle that its operations should not block anymore as there is no chance for their successful termination in the nearest future (i.e., as long as the handle is inactive). 87 // 88 // 2. When the handle is `disabled`, it does not inform the sink about passed time but immediately reports back, thus not blocking execution of other handles. 89 // 90 // 3. The handle is not allowed to resume the execution after reporting a break, without the explicit permission obtained from the time source. 91 // This is why the call of `Request` waits for the `UnblockHandle` when executed in a blocking state. 92 // Once the permission is granted, the handle uses what is left from the previous quantum instead of waiting for a new one. 93 // 94 // 4. Latching is needed to ensure that the handle will not become disabled/re-enabled in an arbitrary moment. 95 // As described in (2), the disabled handle does not synchronize the sink side, so it cannot switch state when the sink is in progress of an execution and the sink cannot resume execution when the source side is in progress. 96 // It is possible to defer changing value of `Enabled` by using `DeferredEnabled` property - their values will be automatically synced (i.e., `Enabled` will get `DeferredEnabled` value) when unlatching the handle. 97 // ------- 98 99 /// <summary> 100 /// Creates a new time handle and associates it with <paramref name="timeSource"/>. 101 /// </summary> TimeHandle(ITimeSource timeSource, ITimeSink timeSink)102 public TimeHandle(ITimeSource timeSource, ITimeSink timeSink) 103 { 104 innerLock = new object(); 105 enabled = true; 106 DeferredEnabled = true; 107 108 TimeSource = timeSource; 109 110 // we should not assign this handle to TimeSink as the source might not be configured properly yet 111 TimeSink = timeSink; 112 113 Reset(); 114 this.Trace(); 115 } 116 Reset()117 public void Reset() 118 { 119 lock(innerLock) 120 { 121 DebugHelper.Assert(TimeSource.ElapsedVirtualTime >= TotalElapsedTime, $"Trying to move time handle back in time from: {TotalElapsedTime} to {TimeSource.ElapsedVirtualTime}"); 122 TotalElapsedTime = TimeSource.ElapsedVirtualTime; 123 } 124 } 125 126 /// <summary> 127 /// Grants a time interval to <see cref="ITimeSink"/>. 128 /// </summary> 129 /// <remarks> 130 /// This method is called by <see cref="ITimeSource"/> and results in unblocking execution of all registered <see cref="ITimeSink"/> for granted period. 131 /// It is illegal to call this method twice in a row. It must be followed by calling <see cref="WaitUntilDone"/>. 132 /// </remarks> GrantTimeInterval(TimeInterval interval)133 public void GrantTimeInterval(TimeInterval interval) 134 { 135 this.Trace($"{interval.Ticks}"); 136 lock(innerLock) 137 { 138 DebugHelper.Assert(IsReadyForNewTimeGrant, "Interval granted, but the handle is not ready for a new one."); 139 sourceSideInProgress = true; 140 141 intervalGranted = interval; 142 143 if(enabled) 144 { 145 this.Trace(); 146 grantPending = true; 147 Monitor.PulseAll(innerLock); 148 } 149 else 150 { 151 this.Trace(); 152 // if the handle is not enabled there is a special way of handling new time grants: 153 // they are not reported to the sink and the following 'WaitUntilDone' returns immediately behaving like the whole time was used up; 154 // we must make sure that the handle is not enabled before the next 'WaitUntilDone' because it could change its result 155 Latch(); 156 deferredUnlatch = true; 157 } 158 } 159 this.Trace(); 160 } 161 162 /// <summary> 163 /// Allows to continue execution of previously granted time interval. 164 /// </summary> 165 /// <remarks> 166 /// This method is called by <see cref="ITimeSource"/> and results in unblocking execution of all registered <see cref="ITimeSink"/> in order to finish execution of previously granted period. 167 /// It is illegal to call this method twice in a row. It must be followed by calling <see cref="WaitUntilDone"/>. 168 /// </remarks> UnblockHandle()169 public bool UnblockHandle() 170 { 171 this.Trace(); 172 lock(innerLock) 173 { 174 DebugHelper.Assert(isBlocking || !enabled, "This handle should be blocking or disabled"); 175 176 if(!waitsToBeUnblocked) 177 { 178 return false; 179 } 180 181 waitsToBeUnblocked = false; 182 grantPending = true; 183 184 Monitor.PulseAll(innerLock); 185 return true; 186 } 187 } 188 189 /// <summary> 190 /// Used by the slave to requests a new time interval from the source. 191 /// This method blocks current thread until the time interval is granted. 192 /// </summary> 193 /// <remarks> 194 /// This method will return immediately when the handle is disabled or detached. 195 /// It is illegal to call this method twice in a row if the first call was successful (returned true). It must always be followed by calling <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">. 196 /// </remarks> 197 /// <returns> 198 /// True if the interval was granted or False when this call was interrupted as a result of detaching or disabling. 199 /// If it returned true, <paramref name="interval"> contains the amount of virtual time to be used by the sink. It is the sum of time interval granted by the source (using <see cref="GrantInterval">) and a time left reported previously by <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">. 200 /// If it returned false, the time interval is not granted and it is illegal to report anything back using <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">. 201 /// </returns> RequestTimeInterval(out TimeInterval interval)202 public bool RequestTimeInterval(out TimeInterval interval) 203 { 204 this.Trace(); 205 lock(innerLock) 206 { 207 DebugHelper.Assert(!sinkSideInProgress, "Requested a new time interval, but the previous one is still processed."); 208 209 var result = true; 210 if(!Enabled || interrupt) 211 { 212 result = false; 213 } 214 else if(isBlocking && SourceSideActive) 215 { 216 if(changingEnabled) 217 { 218 // we test `changingEnabled` here to avoid starvation: 219 // in order to change state of `Enabled` property the handle must not be latched, 220 // so the operation blocks until `latchLevel` drops down to 0; 221 // calling this method (`RequestTimeInterval`) when the handle is in a blocking state results 222 // in latching it temporarily until `WaitUntilDone` is called; 223 // this temporary latching/unlatching together with normal latching/unlatching in a short loop 224 // can cause `latchLevel` to fluctuate from 1 to 2 never allowing the operation modifying `Enabled` to finish 225 result = false; 226 } 227 else 228 { 229 // we check SourceSideActive here as otherwise unblocking will not succeed anyway 230 DebugHelper.Assert(!grantPending, "New grant not expected when blocked."); 231 DebugHelper.Assert(!waitsToBeUnblocked, "Should not wait to be unblocked"); 232 233 // we cannot latch again when deferredUnlatch is still on as we could overwrite it and never unlatch again 234 innerLock.WaitWhile(() => deferredUnlatch && SourceSideActive && !interrupt, "Waiting for previous unlatch"); 235 if(!SourceSideActive || interrupt) 236 { 237 result = false; 238 } 239 else 240 { 241 this.Trace("Asking time source to unblock the time handle"); 242 // latching here is to protect against disabling Enabled that would lead to making IsBlocking false while waiting for unblocking this handle 243 Latch(); 244 245 waitsToBeUnblocked = true; 246 innerLock.WaitWhile(() => waitsToBeUnblocked && SourceSideActive && !interrupt, "Waiting to be unblocked"); 247 if(!SourceSideActive || interrupt) 248 { 249 DebugHelper.Assert(waitsToBeUnblocked, "Expected only one condition to change"); 250 251 Unlatch(); 252 waitsToBeUnblocked = false; 253 result = false; 254 255 this.Trace("Unblocking handle is not allowed, quitting"); 256 } 257 else 258 { 259 DebugHelper.Assert(!waitsToBeUnblocked, "Should not wait to be unblocked here"); 260 DebugHelper.Assert(!deferredUnlatch, "Unexpected value of deferredUnlatch"); 261 262 deferredUnlatch = true; 263 recentlyUnblocked = true; 264 isBlocking = false; 265 266 this.Trace("Handle unblocked"); 267 } 268 } 269 } 270 } 271 else if(!grantPending) 272 { 273 // wait until a new time interval is granted or this handle is disabled/deactivated 274 innerLock.WaitWhile(() => !grantPending && Enabled && SourceSideActive && !interrupt, "Waiting for a time grant"); 275 result = grantPending && !delayGrant && !interrupt; 276 delayGrant = false; 277 } 278 279 if(!result) 280 { 281 interval = TimeInterval.Empty; 282 } 283 else 284 { 285 interval = intervalGranted + slaveTimeResiduum; 286 DebugHelper.Assert(reportedTimeResiduum == TimeInterval.Empty, "Reported time residuum should be empty at this point"); 287 reportedTimeResiduum = slaveTimeResiduum; 288 slaveTimeResiduum = TimeInterval.Empty; 289 290 sinkSideInProgress = true; 291 grantPending = false; 292 } 293 294 this.Trace($"{result}, {interval.Ticks}"); 295 interrupt = false; 296 return result; 297 } 298 } 299 ReportProgress(TimeInterval progress)300 public void ReportProgress(TimeInterval progress) 301 { 302 if(progress.Ticks == 0) 303 { 304 return; 305 } 306 307 lock(innerLock) 308 { 309 // reportedTimeResiduum represents time that 310 // has been reported, but not yet used; 311 // we cannot report it again 312 if(reportedTimeResiduum >= progress) 313 { 314 reportedTimeResiduum -= progress; 315 return; 316 } 317 if(reportedTimeResiduum != TimeInterval.Empty) 318 { 319 progress -= reportedTimeResiduum; 320 reportedTimeResiduum = TimeInterval.Empty; 321 } 322 323 this.Trace($"Reporting progress: {progress}"); 324 TotalElapsedTime += progress; 325 reportedSoFar += progress; 326 TimeSource.ReportTimeProgress(); 327 } 328 } 329 330 /// <summary> 331 /// Informs a time source that the time interval is used, i.e., no more work can be done without exceeding it, and the sink is ready for the next one. 332 /// </summary> 333 /// <remarks> 334 /// It is possible that some part of granted interval cannot be used in this round. This value must be passed in <paramref name="timeLeft"> parameter. 335 /// It is illegal to call this method without first obtaining the interval using <see cref="RequestTimeInterval">. 336 /// </remarks> 337 /// <param name="timeLeft">Amount of time not used.</param> ReportBackAndContinue(TimeInterval timeLeft)338 public void ReportBackAndContinue(TimeInterval timeLeft) 339 { 340 this.Trace($"{timeLeft.Ticks}"); 341 lock(innerLock) 342 { 343 if(DetachRequested) 344 { 345 return; 346 } 347 348 DebugHelper.Assert(sinkSideInProgress, "Reporting a used time, but it seems that no grant has recently been requested."); 349 sinkSideInProgress = false; 350 351 DebugHelper.Assert(slaveTimeResiduum == TimeInterval.Empty, "Time residuum should be empty here."); 352 slaveTimeResiduum = timeLeft; 353 intervalToReport = intervalGranted; 354 355 reportPending = true; 356 357 Monitor.PulseAll(innerLock); 358 this.Trace(); 359 } 360 ReportedBack?.Invoke(); 361 } 362 363 /// <summary> 364 /// Informs a time source that the time sink interrupted the execution before finishing the granted interval. 365 /// In order to finish the job it is required to call <see cref="RequestTimeInterval"> followed by <see cref="ReportBackAndContinue">. 366 /// </summary> 367 /// <remarks> 368 /// No new time interval will be granted to this and all other time sinks in the time domain until <see cref="ReportBackAndContinue"> is called. 369 /// It is illegal to call this method without first obtaining the interval using <see cref="RequestTimeInterval">. 370 /// </remarks> 371 /// <param name="intervalLeft">Amount of time not used.</param> ReportBackAndBreak(TimeInterval timeLeft)372 public void ReportBackAndBreak(TimeInterval timeLeft) 373 { 374 this.Trace($"{timeLeft.Ticks}"); 375 lock(innerLock) 376 { 377 if(DetachRequested) 378 { 379 return; 380 } 381 382 DebugHelper.Assert(sinkSideInProgress, "Reporting a used time, but it seems that no grant has recently been requested."); 383 sinkSideInProgress = false; 384 385 intervalToReport = intervalGranted - timeLeft; 386 intervalGranted = timeLeft; 387 isBlocking = true; 388 389 reportPending = true; 390 391 Monitor.PulseAll(innerLock); 392 this.Trace(); 393 } 394 ReportedBack?.Invoke(); 395 } 396 397 /// <summary> 398 /// Informs a time source that any available time is used. 399 /// </summary> 400 /// <remarks> 401 /// It is illegal to call this method if an interval is obtained, i.e. between calls to <see cref="RequestTimeInterval"> and <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">. 402 /// </remarks> TrySkipToSyncPoint(out TimeInterval intervalSkipped)403 public bool TrySkipToSyncPoint(out TimeInterval intervalSkipped) 404 { 405 lock(innerLock) 406 { 407 if(!RequestTimeInterval(out intervalSkipped)) 408 { 409 return false; 410 } 411 ReportBackAndContinue(TimeInterval.Empty); 412 return true; 413 } 414 } 415 416 /// <summary> 417 /// Disables the handle and requests detaching it from <see cref="ITimeSource"/>. 418 /// </summary> Dispose()419 public void Dispose() 420 { 421 this.Trace(); 422 lock(innerLock) 423 { 424 SinkSideActive = false; 425 SourceSideActive = false; 426 427 // this operation is blocking if the handle is latched 428 // it does not allow the handle to be disposed when in use 429 Enabled = false; 430 431 DetachRequested = true; 432 sinkSideInProgress = false; 433 sourceSideInProgress = false; 434 reportPending = false; 435 intervalToReport = intervalGranted; 436 Monitor.PulseAll(innerLock); 437 438 PauseRequested = null; 439 StartRequested = null; 440 } 441 this.Trace(); 442 } 443 444 /// <summary> 445 /// Blocks the execution of current thread until the slave reports back. 446 /// </summary> 447 /// <param name="intervalUsed">Amount of virtual time that passed from the perspective of a slave.</param> 448 /// <returns> 449 /// A structure containing two booleans: 450 /// * IsDone: True if the slave completed all the work or false if the execution was interrupted (and it's blocking now). 451 /// * IsUnblockedRecently: True if the handle has recently (i.e., since the last call to `WaitUntilDone`) been unblocked - it resumed the execution after reporting break. 452 /// </returns> WaitUntilDone(out TimeInterval intervalUsed)453 public WaitResult WaitUntilDone(out TimeInterval intervalUsed) 454 { 455 this.Trace(); 456 lock(innerLock) 457 { 458 Debugging.DebugHelper.Assert(sourceSideInProgress, "About to wait until time is used, but it seems none has recently been granted."); 459 460 innerLock.WaitWhile(() => sinkSideInProgress || (SinkSideActive && grantPending), "Waiting until time is used."); 461 462 intervalUsed = enabled ? intervalToReport : intervalGranted; 463 intervalToReport = TimeInterval.Empty; 464 465 var isDone = !isBlocking; 466 if(enabled && !SinkSideActive && !reportPending) 467 { 468 Debugging.DebugHelper.Assert(!deferredUnlatch, "Unexpected state of deferredUnlatch"); 469 470 // 'false' value of 'SinkSideActive' means that there is no point hanging and waiting in this function as there is no chance of unblocking in the nearest future 471 // in such situation just return 'false' simulating blocked state 472 // the only exception is if `reportPending` is set which means that we should first return value as set be the previous Report{Continue,Break} 473 474 this.Trace("Forcing result to be false"); 475 476 // being here means that the sink has not yet 477 // seen the granted interval, so we can act 478 // as if it called ReportBackAndBreak 479 grantPending = false; 480 isBlocking = true; 481 intervalUsed = TimeInterval.Empty; 482 isDone = false; 483 // intervalGranted does not change 484 485 Monitor.PulseAll(innerLock); 486 this.Trace(); 487 } 488 489 Debugging.DebugHelper.Assert(reportedSoFar <= intervalUsed); 490 // here we report the remaining part of granted time 491 reportedTimeResiduum = TimeInterval.Empty; 492 ReportProgress(intervalUsed - reportedSoFar); 493 reportedSoFar = TimeInterval.Empty; 494 495 reportPending = false; 496 497 if(isDone) 498 { 499 sourceSideInProgress = false; 500 } 501 502 var result = new WaitResult(isDone, recentlyUnblocked); 503 recentlyUnblocked = false; 504 if(deferredUnlatch) 505 { 506 deferredUnlatch = false; 507 Unlatch(); 508 } 509 510 Monitor.PulseAll(innerLock); 511 512 this.Trace($"Reporting {intervalUsed.Ticks} ticks used. Local elapsed virtual time is {TotalElapsedTime.Ticks} ticks."); 513 this.Trace(result.ToString()); 514 return result; 515 } 516 } 517 518 /// <summary> 519 /// Latches the time handle, i.e., blocks any calls resulting in changing <see cref="Enabled"> property until <see cref="Unlatch"> is called. 520 /// </summary> 521 /// <remarks> 522 /// This method is intended for use by time source to ensure that all asynchronous changes to the time handle's state are masked. 523 /// </remarks> Latch()524 public void Latch() 525 { 526 this.Trace(); 527 lock(innerLock) 528 { 529 latchLevel++; 530 this.Trace($"Time handle latched; current level is {latchLevel}"); 531 } 532 this.Trace(); 533 } 534 535 /// <summary> 536 /// Unlatches the time handle. 537 /// </summary> 538 /// <remarks> 539 /// Calling this method will result in unblocking all threads wanting to change <see cref="Enabled"> property. 540 /// </remarks> Unlatch()541 public void Unlatch() 542 { 543 this.Trace(); 544 lock(innerLock) 545 { 546 DebugHelper.Assert(latchLevel > 0, "Tried to unlatch not latched handle"); 547 latchLevel--; 548 this.Trace($"Time handle unlatched; current level is {latchLevel}"); 549 // since there is one place when we wait for latch to be equal to 1, we have to pulse more often than only when latchLevel is 0 550 Monitor.PulseAll(innerLock); 551 552 if(latchLevel == 0) 553 { 554 Enabled = DeferredEnabled; 555 } 556 } 557 this.Trace(); 558 } 559 560 /// <summary> 561 /// Calls <see cref="PauseRequested"/> event. 562 /// </summary> RequestPause()563 public void RequestPause() 564 { 565 this.Trace(); 566 PauseRequested?.Invoke(); 567 } 568 569 /// <summary> 570 /// Calls <see cref="StartRequested"/> event. 571 /// </summary> RequestStart()572 public void RequestStart() 573 { 574 this.Trace(); 575 StartRequested?.Invoke(); 576 } 577 578 /// <summary> 579 /// Interrupts the current or next call to <see cref="RequestTimeInterval"/> causing it to return 'false' immediately. 580 /// The caller of this method should check if lock was acquired within specified timeout and retry if not. 581 /// </summary> 582 /// <param name="success">True if lock was acquired within timeout, false otherwise</param> Interrupt(ref bool success, int millisecondsTimeout = Timeout.Infinite)583 public void Interrupt(ref bool success, int millisecondsTimeout = Timeout.Infinite) 584 { 585 var timeout = TimeSpan.FromMilliseconds(millisecondsTimeout); 586 587 try 588 { 589 Monitor.TryEnter(innerLock, timeout, ref success); 590 if(success) 591 { 592 interrupt = true; 593 Monitor.PulseAll(innerLock); 594 } 595 } 596 finally 597 { 598 // Ensure that the lock is released. 599 if(success) 600 { 601 Monitor.Exit(innerLock); 602 } 603 } 604 } 605 606 /// <summary> 607 /// Sets the value indicating if the handle is enabled, i.e., is sink interested in the time information. 608 /// </summary> 609 /// <remarks> 610 /// When the handle is disabled it behaves as if the execution on the sink was instantaneous - it never blocks other threads, but keeps track of virtual time. 611 /// Setting this property might be blocking (if the time handle is currently latched). 612 /// Disabled handle will not block on <see cref="WaitUntilDone">, returning 'true' immediately. 613 /// Disabling the handle interrupts current <see cref="RequestTimeInterval"> call and makes all following calls return immediately with 'false'. 614 /// </remarks> 615 public bool Enabled 616 { 617 get 618 { 619 return enabled; 620 } 621 622 set 623 { 624 lock(innerLock) 625 { 626 if(enabled == value) 627 { 628 return; 629 } 630 631 changingEnabled = true; 632 this.Trace("About to wait for unlatching the time handle"); 633 innerLock.WaitWhile(() => latchLevel > 0, "Waiting for unlatching the time handle"); 634 635 this.Trace($"Enabled value changed: {enabled} -> {value}"); 636 enabled = value; 637 DeferredEnabled = value; 638 changingEnabled = false; 639 if(!enabled) 640 { 641 Monitor.PulseAll(innerLock); 642 643 // we have just disabled the handle - it needs to be reset it to a state like after `ReportBackAndContinue` with not time left 644 if(isBlocking) 645 { 646 slaveTimeResiduum = TimeInterval.Empty; 647 reportedTimeResiduum = TimeInterval.Empty; 648 intervalToReport = intervalGranted; 649 reportPending = true; 650 isBlocking = false; 651 652 Monitor.PulseAll(innerLock); 653 this.Trace(); 654 } 655 } 656 else 657 { 658 TimeSource.ReportHandleActive(); 659 RequestStart(); 660 } 661 } 662 } 663 } 664 665 /// <summary> 666 /// Gets or sets the value indicating if this handle is active from the source perspective, i.e., will source grant new time in the nearest future. 667 /// </summary> 668 public bool SourceSideActive 669 { 670 get 671 { 672 return sourceSideActive; 673 } 674 675 set 676 { 677 lock(innerLock) 678 { 679 this.Trace($"{value}"); 680 sourceSideActive = value; 681 if(!sourceSideActive) 682 { 683 // there is a code that waits for a change of `SourceSideActive` value using `WaitWhile`, so we must call `PulseAll` here 684 Monitor.PulseAll(innerLock); 685 } 686 } 687 } 688 } 689 690 /// <summary> 691 /// Sets the value indicating if this handle is active from the sink perspective, i.e., will sink be requesting a time grant in the nearest future. 692 /// </summary> 693 /// <remarks> 694 /// As long as the handle is not active from the sink perspective all <see cref="WaitUntilDone"> calls will return immediately with 'false'. 695 /// </remarks> 696 public bool SinkSideActive 697 { 698 get 699 { 700 return sinkSideActive; 701 } 702 703 set 704 { 705 lock(innerLock) 706 { 707 DebugHelper.Assert(!sinkSideInProgress, "Should not change sink side active state when sink is in progress"); 708 709 this.Trace($"{value}"); 710 sinkSideActive = value; 711 if(!sinkSideActive) 712 { 713 Monitor.PulseAll(innerLock); 714 } 715 else 716 { 717 // we must inform the source that we became active so it can spin its loop again 718 TimeSource.ReportHandleActive(); 719 } 720 } 721 } 722 } 723 724 /// <summary> 725 /// Gets the flag indicating if the new time interval can be granted to this handle. 726 /// </summary> 727 /// <remarks> 728 /// In order for a handle to be ready to accept a new time grant, following conditions must be met: 729 /// * previously granted time must be completely used, 730 /// * detaching must not be requested. 731 /// </remarks> 732 public bool IsReadyForNewTimeGrant 733 { 734 get 735 { 736 lock(innerLock) 737 { 738 var res = !sourceSideInProgress && !DetachRequested; 739 this.Trace($"Reading IsReadyForNewTimeGrant: {res}; sourceSideInProgress={sourceSideInProgress}, DetachRequested={DetachRequested}"); 740 return res; 741 } 742 } 743 } 744 745 /// <summary> 746 /// This flag set guarantees the next call to <see cref="UnblockHandle"> to succeed. 747 /// </summary> 748 public bool IsReadyToBeUnblocked => waitsToBeUnblocked || !enabled; 749 750 /// <summary> 751 /// Gets the flag indicating if this time handle is disposed and ready to be removed from its time source. 752 /// </summary> 753 public bool DetachRequested { get; private set; } 754 755 /// <summary> 756 /// Gets the reference to the time source associated with this handle. 757 /// </summary> 758 public ITimeSource TimeSource { get; private set; } 759 760 /// <summary> 761 /// Gets the reference to the time sink associated with this handle. 762 /// </summary> 763 public ITimeSink TimeSink { get; private set; } 764 765 /// <summary> 766 /// Gets the amount of virtual time that passed from the perspective of this handle. 767 /// </summary> 768 public TimeInterval TotalElapsedTime { get; private set; } 769 770 /// <summary> 771 /// The value of the enabled property that will be set on the nearest call to <see cref="Unlatch"> method. 772 /// </summary> 773 public bool DeferredEnabled { get; set; } 774 775 /// <summary> 776 /// Delay time grant to sink by one call to <see cref="RequestTimeInterval"> when waiting for a time grant from source. 777 /// </summary> 778 public bool DelayGrant 779 { 780 get 781 { 782 return delayGrant; 783 } 784 785 set 786 { 787 lock(innerLock) 788 { 789 delayGrant = value; 790 } 791 } 792 } 793 794 /// <summary> 795 /// Is set by the source, indicates whether the sink has used all of the time interval available to the source. 796 /// </summary> 797 public bool IsDone 798 { 799 get 800 { 801 return isDone; 802 } 803 804 set 805 { 806 isDone = value; 807 } 808 } 809 810 /// <summary> 811 /// Informs the sink that the source wants to pause its execution. 812 /// </summary> 813 /// <remarks> 814 /// The sink can react to it in the middle of a granted period and pause instantly. 815 /// </remarks> 816 public event Action PauseRequested; 817 818 /// <summary> 819 /// Informs the sink that the source is about to (re)start its execution, so it should start the dispatcher thread and get ready for new grants. 820 /// </summary> 821 public event Action StartRequested; 822 823 /// <summary> 824 /// Call when the sink calls ReportBackAndContinue or ReportBackAndBreak. 825 /// </summary> 826 public event Action ReportedBack; 827 828 [Antmicro.Migrant.Hooks.PreSerialization] VerifyStateBeforeSerialization()829 private void VerifyStateBeforeSerialization() 830 { 831 lock(innerLock) 832 { 833 DebugHelper.Assert(!sinkSideInProgress, "Trying to save a time handle that processes a time grant"); 834 } 835 } 836 837 /// <summary> 838 /// Indicates that there is a time granted, but not yet successfully waited for (i.e., with 'true' result). 839 /// </summary> 840 private bool sourceSideInProgress; 841 private bool isBlocking; 842 /// <summary> 843 /// Indicates that there is a new time granted but not yet requested. 844 /// </summary> 845 private bool grantPending; 846 private bool sinkSideInProgress; 847 private bool reportPending; 848 849 /// <summary> 850 /// The amount of time granted last time. 851 /// </summary> 852 private TimeInterval intervalGranted; 853 /// <summary> 854 /// The amount of time to return on next <see cref="WaitUntilDone"/>. 855 /// </summary> 856 private TimeInterval intervalToReport; 857 /// <summary> 858 /// The amount of time left from previous grant that was not used but reported back in <see cref="WaitUntilDone"/>. 859 /// </summary> 860 private TimeInterval slaveTimeResiduum; 861 /// <summary> 862 /// The amount of time left from previous grant that was reported in <see cref="ReportProgress"/>. 863 /// </summary> 864 private TimeInterval reportedTimeResiduum; 865 /// <summary> 866 /// Flag is set when the handle is actively waiting to be unblocked 867 /// </summary> 868 private bool waitsToBeUnblocked; 869 ///<summary> 870 /// The amount of time already reported since last WaitUntilDone. 871 ///</summary> 872 private TimeInterval reportedSoFar; 873 874 private bool enabled; 875 private bool sinkSideActive; 876 private bool sourceSideActive; 877 878 private bool changingEnabled; 879 private int latchLevel; 880 private bool deferredUnlatch; 881 private bool recentlyUnblocked; 882 private bool delayGrant; 883 private bool interrupt; 884 private volatile bool isDone; 885 886 private readonly object innerLock; 887 888 public struct WaitResult 889 { WaitResultAntmicro.Renode.Time.TimeHandle.WaitResult890 public WaitResult(bool isDone, bool isUnblockedRecently) : this() 891 { 892 IsDone = isDone; 893 IsUnblockedRecently = isUnblockedRecently; 894 } 895 ToStringAntmicro.Renode.Time.TimeHandle.WaitResult896 public override string ToString() 897 { 898 return $"[WaitResult(isDone: {IsDone}, isActivatedRecently: {IsUnblockedRecently})]"; 899 } 900 901 public bool IsDone { get; private set; } 902 public bool IsUnblockedRecently { get; private set; } 903 } 904 } 905 } 906