github.com/weaviate/weaviate@v1.24.6/entities/cyclemanager/cyclecallbackgroup_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package cyclemanager 13 14 import ( 15 "context" 16 "sync/atomic" 17 "testing" 18 "time" 19 20 "github.com/sirupsen/logrus/hooks/test" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestCycleCallback_Parallel(t *testing.T) { 26 logger, _ := test.NewNullLogger() 27 shouldNotAbort := func() bool { return false } 28 29 t.Run("no callbacks", func(t *testing.T) { 30 var executed bool 31 32 callbacks := NewCallbackGroup("id", logger, 2) 33 34 executed = callbacks.CycleCallback(shouldNotAbort) 35 36 assert.False(t, executed) 37 }) 38 39 t.Run("2 executable callbacks", func(t *testing.T) { 40 executedCounter1 := 0 41 callback1 := func(shouldAbort ShouldAbortCallback) bool { 42 time.Sleep(50 * time.Millisecond) 43 executedCounter1++ 44 return true 45 } 46 executedCounter2 := 0 47 callback2 := func(shouldAbort ShouldAbortCallback) bool { 48 time.Sleep(25 * time.Millisecond) 49 executedCounter2++ 50 return true 51 } 52 var executed bool 53 var d time.Duration 54 55 callbacks := NewCallbackGroup("id", logger, 2) 56 callbacks.Register("c1", callback1) 57 callbacks.Register("c2", callback2) 58 59 start := time.Now() 60 executed = callbacks.CycleCallback(shouldNotAbort) 61 d = time.Since(start) 62 63 assert.True(t, executed) 64 assert.Equal(t, 1, executedCounter1) 65 assert.Equal(t, 1, executedCounter2) 66 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 67 }) 68 69 t.Run("2 non-executable callbacks", func(t *testing.T) { 70 executedCounter1 := 0 71 callback1 := func(shouldAbort ShouldAbortCallback) bool { 72 time.Sleep(10 * time.Millisecond) 73 executedCounter1++ 74 return false 75 } 76 executedCounter2 := 0 77 callback2 := func(shouldAbort ShouldAbortCallback) bool { 78 time.Sleep(10 * time.Millisecond) 79 executedCounter2++ 80 return false 81 } 82 var executed bool 83 var d time.Duration 84 85 callbacks := NewCallbackGroup("id", logger, 2) 86 callbacks.Register("c1", callback1) 87 callbacks.Register("c2", callback2) 88 89 start := time.Now() 90 executed = callbacks.CycleCallback(shouldNotAbort) 91 d = time.Since(start) 92 93 assert.False(t, executed) 94 assert.Equal(t, 1, executedCounter1) 95 assert.Equal(t, 1, executedCounter2) 96 assert.GreaterOrEqual(t, d, 10*time.Millisecond) 97 }) 98 99 t.Run("3 executable callbacks, not all executed due to should abort", func(t *testing.T) { 100 executedCounter1 := 0 101 callback1 := func(shouldAbort ShouldAbortCallback) bool { 102 time.Sleep(25 * time.Millisecond) 103 executedCounter1++ 104 return true 105 } 106 executedCounter2 := 0 107 callback2 := func(shouldAbort ShouldAbortCallback) bool { 108 time.Sleep(25 * time.Millisecond) 109 executedCounter2++ 110 return true 111 } 112 executedCounter3 := 0 113 callback3 := func(shouldAbort ShouldAbortCallback) bool { 114 time.Sleep(25 * time.Millisecond) 115 executedCounter3++ 116 return true 117 } 118 // due to async calls of shouldAbort callback by main for loop 119 // and goroutines reading from shared channel it is hard to 120 // establish order of calls. 121 // with 3 callbacks and shouldAbort returning true on 6th call 122 // 1 or 2 callbacks should be executed, but not all 3. 123 shouldAbortCounter := uint32(0) 124 shouldAbort := func() bool { 125 return atomic.AddUint32(&shouldAbortCounter, 1) > 5 126 } 127 var executed bool 128 var d time.Duration 129 130 callbacks := NewCallbackGroup("id", logger, 2) 131 callbacks.Register("c1", callback1) 132 callbacks.Register("c2", callback2) 133 callbacks.Register("c3", callback3) 134 135 start := time.Now() 136 executed = callbacks.CycleCallback(shouldAbort) 137 d = time.Since(start) 138 139 assert.True(t, executed) 140 totalExecuted := executedCounter1 + executedCounter2 + executedCounter3 141 assert.Greater(t, totalExecuted, 0) 142 assert.Less(t, totalExecuted, 3) 143 assert.GreaterOrEqual(t, d, 25*time.Millisecond) 144 }) 145 146 t.Run("register new while executing", func(t *testing.T) { 147 executedCounter1 := 0 148 callback1 := func(shouldAbort ShouldAbortCallback) bool { 149 time.Sleep(50 * time.Millisecond) 150 executedCounter1++ 151 return true 152 } 153 executedCounter2 := 0 154 callback2 := func(shouldAbort ShouldAbortCallback) bool { 155 time.Sleep(50 * time.Millisecond) 156 executedCounter2++ 157 return true 158 } 159 executedCounter3 := 0 160 callback3 := func(shouldAbort ShouldAbortCallback) bool { 161 time.Sleep(50 * time.Millisecond) 162 executedCounter3++ 163 return true 164 } 165 executedCounter4 := 0 166 callback4 := func(shouldAbort ShouldAbortCallback) bool { 167 time.Sleep(50 * time.Millisecond) 168 executedCounter4++ 169 return true 170 } 171 chStarted := make(chan struct{}, 1) 172 chFinished := make(chan struct{}, 1) 173 var executed bool 174 var d time.Duration 175 176 callbacks := NewCallbackGroup("id", logger, 2) 177 callbacks.Register("c1", callback1) 178 callbacks.Register("c2", callback2) 179 callbacks.Register("c3", callback3) 180 181 // register 4th callback while other are executed, 182 // 183 // while 1st and 2nd are being processed (50ms), 184 // 3rd is waiting for available routine (without 3rd callback loop would be finished) 185 // 4th is registered (25ms) to be called next along with 3rd 186 go func() { 187 chStarted <- struct{}{} 188 start := time.Now() 189 executed = callbacks.CycleCallback(shouldNotAbort) 190 d = time.Since(start) 191 chFinished <- struct{}{} 192 }() 193 <-chStarted 194 time.Sleep(25 * time.Millisecond) 195 callbacks.Register("c4", callback4) 196 <-chFinished 197 198 assert.True(t, executed) 199 assert.Equal(t, 1, executedCounter1) 200 assert.Equal(t, 1, executedCounter2) 201 assert.Equal(t, 1, executedCounter3) 202 assert.Equal(t, 1, executedCounter4) 203 assert.GreaterOrEqual(t, d, 100*time.Millisecond) 204 }) 205 206 t.Run("run with intervals", func(T *testing.T) { 207 ticker := NewFixedTicker(10 * time.Millisecond) 208 intervals2 := NewSeriesIntervals([]time.Duration{ 209 10 * time.Millisecond, 30 * time.Millisecond, 50 * time.Millisecond, 210 }) 211 intervals3 := NewFixedIntervals(60 * time.Millisecond) 212 now := time.Now() 213 214 executionTimes1 := []time.Duration{} 215 callback1 := func(shouldAbort ShouldAbortCallback) bool { 216 executionTimes1 = append(executionTimes1, time.Since(now)) 217 return true 218 } 219 executionCounter2 := 0 220 executionTimes2 := []time.Duration{} 221 callback2 := func(shouldAbort ShouldAbortCallback) bool { 222 executionCounter2++ 223 executionTimes2 = append(executionTimes2, time.Since(now)) 224 // reports executed every 3 calls, should result in 10, 30, 50, 50, 10, 30, 50, 50, ... intervals 225 return executionCounter2%4 == 0 226 } 227 executionTimes3 := []time.Duration{} 228 callback3 := func(shouldAbort ShouldAbortCallback) bool { 229 executionTimes3 = append(executionTimes3, time.Since(now)) 230 return true 231 } 232 233 callbacks := NewCallbackGroup("id", logger, 2) 234 // should be called on every tick, with 10 intervals 235 callbacks.Register("c1", callback1) 236 // should be called with 10, 30, 50, 50, 10, 30, 50, 50, ... intervals 237 callbacks.Register("c2", callback2, WithIntervals(intervals2)) 238 // should be called with 60, 60, ... intervals 239 callbacks.Register("c3", callback3, WithIntervals(intervals3)) 240 241 cm := NewManager(ticker, callbacks.CycleCallback, logger) 242 cm.Start() 243 time.Sleep(400 * time.Millisecond) 244 cm.StopAndWait(context.Background()) 245 246 // within 400 ms c1 should be called at least 30x 247 require.GreaterOrEqual(t, len(executionTimes1), 30) 248 // 1st call on 1st tick after 10ms 249 sumDuration := time.Duration(10) 250 for i := 0; i < 30; i++ { 251 assert.GreaterOrEqual(t, executionTimes1[i], sumDuration) 252 sumDuration += 10 * time.Millisecond 253 } 254 255 // within 400 ms c2 should be called at least 8x 256 require.GreaterOrEqual(t, len(executionTimes2), 8) 257 // 1st call on 1st tick after 10ms 258 sumDuration = time.Duration(0) 259 for i := 0; i < 8; i++ { 260 assert.GreaterOrEqual(t, executionTimes2[i], sumDuration) 261 switch (i + 1) % 4 { 262 case 0: 263 sumDuration += 10 * time.Millisecond 264 case 1: 265 sumDuration += 30 * time.Millisecond 266 case 2, 3: 267 sumDuration += 50 * time.Millisecond 268 } 269 } 270 271 // within 400 ms c3 should be called at least 6x 272 require.GreaterOrEqual(t, len(executionTimes3), 6) 273 // 1st call on 1st tick after 10ms 274 sumDuration = time.Duration(0) 275 for i := 0; i < 6; i++ { 276 assert.GreaterOrEqual(t, executionTimes3[i], sumDuration) 277 sumDuration += 60 * time.Millisecond 278 } 279 }) 280 } 281 282 func TestCycleCallback_Parallel_Unregister(t *testing.T) { 283 ctx := context.Background() 284 logger, _ := test.NewNullLogger() 285 shouldNotAbort := func() bool { return false } 286 287 t.Run("1 executable callback, 1 unregistered", func(t *testing.T) { 288 executedCounter := 0 289 callback := func(shouldAbort ShouldAbortCallback) bool { 290 time.Sleep(50 * time.Millisecond) 291 executedCounter++ 292 return true 293 } 294 var executed bool 295 var d time.Duration 296 297 callbacks := NewCallbackGroup("id", logger, 2) 298 ctrl := callbacks.Register("c1", callback) 299 require.Nil(t, ctrl.Unregister(ctx)) 300 301 start := time.Now() 302 executed = callbacks.CycleCallback(shouldNotAbort) 303 d = time.Since(start) 304 305 assert.False(t, executed) 306 assert.Equal(t, 0, executedCounter) 307 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 308 }) 309 310 t.Run("2 executable callbacks, 2 unregistered", func(t *testing.T) { 311 executedCounter1 := 0 312 callback1 := func(shouldAbort ShouldAbortCallback) bool { 313 time.Sleep(50 * time.Millisecond) 314 executedCounter1++ 315 return true 316 } 317 executedCounter2 := 0 318 callback2 := func(shouldAbort ShouldAbortCallback) bool { 319 time.Sleep(25 * time.Millisecond) 320 executedCounter2++ 321 return true 322 } 323 var executed bool 324 var d time.Duration 325 326 callbacks := NewCallbackGroup("id", logger, 2) 327 ctrl1 := callbacks.Register("c1", callback1) 328 ctrl2 := callbacks.Register("c2", callback2) 329 require.Nil(t, ctrl1.Unregister(ctx)) 330 require.Nil(t, ctrl2.Unregister(ctx)) 331 332 start := time.Now() 333 executed = callbacks.CycleCallback(shouldNotAbort) 334 d = time.Since(start) 335 336 assert.False(t, executed) 337 assert.Equal(t, 0, executedCounter1) 338 assert.Equal(t, 0, executedCounter2) 339 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 340 }) 341 342 t.Run("2 executable callbacks, 1 unregistered", func(t *testing.T) { 343 executedCounter1 := 0 344 callback1 := func(shouldAbort ShouldAbortCallback) bool { 345 time.Sleep(50 * time.Millisecond) 346 executedCounter1++ 347 return true 348 } 349 executedCounter2 := 0 350 callback2 := func(shouldAbort ShouldAbortCallback) bool { 351 time.Sleep(25 * time.Millisecond) 352 executedCounter2++ 353 return true 354 } 355 var executed bool 356 var d time.Duration 357 358 callbacks := NewCallbackGroup("id", logger, 2) 359 ctrl1 := callbacks.Register("c1", callback1) 360 callbacks.Register("c2", callback2) 361 require.Nil(t, ctrl1.Unregister(ctx)) 362 363 start := time.Now() 364 executed = callbacks.CycleCallback(shouldNotAbort) 365 d = time.Since(start) 366 367 assert.True(t, executed) 368 assert.Equal(t, 0, executedCounter1) 369 assert.Equal(t, 1, executedCounter2) 370 assert.GreaterOrEqual(t, d, 25*time.Millisecond) 371 }) 372 373 t.Run("4 executable callbacks, all unregistered at different time", func(t *testing.T) { 374 executedCounter1 := 0 375 callback1 := func(shouldAbort ShouldAbortCallback) bool { 376 time.Sleep(25 * time.Millisecond) 377 executedCounter1++ 378 return true 379 } 380 executedCounter2 := 0 381 callback2 := func(shouldAbort ShouldAbortCallback) bool { 382 time.Sleep(25 * time.Millisecond) 383 executedCounter2++ 384 return true 385 } 386 executedCounter3 := 0 387 callback3 := func(shouldAbort ShouldAbortCallback) bool { 388 time.Sleep(25 * time.Millisecond) 389 executedCounter3++ 390 return true 391 } 392 executedCounter4 := 0 393 callback4 := func(shouldAbort ShouldAbortCallback) bool { 394 time.Sleep(25 * time.Millisecond) 395 executedCounter4++ 396 return true 397 } 398 var executed1 bool 399 var executed2 bool 400 var executed3 bool 401 var executed4 bool 402 var d1 time.Duration 403 var d2 time.Duration 404 var d3 time.Duration 405 var d4 time.Duration 406 407 callbacks := NewCallbackGroup("id", logger, 2) 408 ctrl1 := callbacks.Register("c1", callback1) 409 ctrl2 := callbacks.Register("c2", callback2) 410 ctrl3 := callbacks.Register("c3", callback3) 411 ctrl4 := callbacks.Register("c4", callback4) 412 require.Nil(t, ctrl3.Unregister(ctx)) 413 414 start := time.Now() 415 executed1 = callbacks.CycleCallback(shouldNotAbort) 416 d1 = time.Since(start) 417 418 require.Nil(t, ctrl1.Unregister(ctx)) 419 420 start = time.Now() 421 executed2 = callbacks.CycleCallback(shouldNotAbort) 422 d2 = time.Since(start) 423 424 require.Nil(t, ctrl4.Unregister(ctx)) 425 426 start = time.Now() 427 executed3 = callbacks.CycleCallback(shouldNotAbort) 428 d3 = time.Since(start) 429 430 require.Nil(t, ctrl2.Unregister(ctx)) 431 432 start = time.Now() 433 executed4 = callbacks.CycleCallback(shouldNotAbort) 434 d4 = time.Since(start) 435 436 assert.True(t, executed1) 437 assert.True(t, executed2) 438 assert.True(t, executed3) 439 assert.False(t, executed4) 440 assert.Equal(t, 1, executedCounter1) 441 assert.Equal(t, 3, executedCounter2) 442 assert.Equal(t, 0, executedCounter3) 443 assert.Equal(t, 2, executedCounter4) 444 assert.GreaterOrEqual(t, d1, 50*time.Millisecond) 445 assert.GreaterOrEqual(t, d2, 25*time.Millisecond) 446 assert.GreaterOrEqual(t, d3, 25*time.Millisecond) 447 assert.GreaterOrEqual(t, d4, 0*time.Millisecond) 448 }) 449 450 t.Run("unregister is waiting till the end of execution", func(t *testing.T) { 451 executedCounter := 0 452 callback := func(shouldAbort ShouldAbortCallback) bool { 453 time.Sleep(50 * time.Millisecond) 454 executedCounter++ 455 return true 456 } 457 chStarted := make(chan struct{}, 1) 458 chFinished := make(chan struct{}, 1) 459 var executed bool 460 var d time.Duration 461 462 callbacks := NewCallbackGroup("id", logger, 2) 463 ctrl := callbacks.Register("c", callback) 464 465 go func() { 466 chStarted <- struct{}{} 467 start := time.Now() 468 executed = callbacks.CycleCallback(shouldNotAbort) 469 d = time.Since(start) 470 chFinished <- struct{}{} 471 }() 472 <-chStarted 473 start := time.Now() 474 time.Sleep(25 * time.Millisecond) 475 require.Nil(t, ctrl.Unregister(ctx)) 476 du := time.Since(start) 477 <-chFinished 478 479 assert.True(t, executed) 480 assert.Equal(t, 1, executedCounter) 481 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 482 assert.GreaterOrEqual(t, du, 40*time.Millisecond) 483 }) 484 485 t.Run("unregister fails due to context timeout", func(t *testing.T) { 486 executedCounter := 0 487 callback := func(shouldAbort ShouldAbortCallback) bool { 488 time.Sleep(50 * time.Millisecond) 489 executedCounter++ 490 return true 491 } 492 chStarted := make(chan struct{}, 1) 493 chFinished := make(chan struct{}, 1) 494 var executed1 bool 495 var executed2 bool 496 var d1 time.Duration 497 var d2 time.Duration 498 499 callbacks := NewCallbackGroup("id", logger, 2) 500 ctrl := callbacks.Register("c", callback) 501 502 go func() { 503 chStarted <- struct{}{} 504 start := time.Now() 505 executed1 = callbacks.CycleCallback(shouldNotAbort) 506 d1 = time.Since(start) 507 chFinished <- struct{}{} 508 }() 509 <-chStarted 510 start := time.Now() 511 time.Sleep(25 * time.Millisecond) 512 ctxTimeout, cancel := context.WithTimeout(ctx, 5*time.Millisecond) 513 defer cancel() 514 require.NotNil(t, ctrl.Unregister(ctxTimeout)) 515 du := time.Since(start) 516 <-chFinished 517 518 go func() { 519 start := time.Now() 520 executed2 = callbacks.CycleCallback(shouldNotAbort) 521 d2 = time.Since(start) 522 chFinished <- struct{}{} 523 }() 524 <-chFinished 525 526 assert.True(t, executed1) 527 assert.True(t, executed2) 528 assert.Equal(t, 2, executedCounter) 529 assert.GreaterOrEqual(t, d1, 50*time.Millisecond) 530 assert.GreaterOrEqual(t, d2, 50*time.Millisecond) 531 assert.GreaterOrEqual(t, du, 30*time.Millisecond) 532 }) 533 534 t.Run("unregister 3rd and 4th while executing", func(t *testing.T) { 535 executedCounter1 := 0 536 callback1 := func(shouldAbort ShouldAbortCallback) bool { 537 time.Sleep(50 * time.Millisecond) 538 executedCounter1++ 539 return true 540 } 541 executedCounter2 := 0 542 callback2 := func(shouldAbort ShouldAbortCallback) bool { 543 time.Sleep(50 * time.Millisecond) 544 executedCounter2++ 545 return true 546 } 547 executedCounter3 := 0 548 callback3 := func(shouldAbort ShouldAbortCallback) bool { 549 time.Sleep(50 * time.Millisecond) 550 executedCounter3++ 551 return true 552 } 553 executedCounter4 := 0 554 callback4 := func(shouldAbort ShouldAbortCallback) bool { 555 time.Sleep(50 * time.Millisecond) 556 executedCounter4++ 557 return true 558 } 559 chStarted := make(chan struct{}, 1) 560 chFinished := make(chan struct{}, 1) 561 var executed bool 562 var d time.Duration 563 564 callbacks := NewCallbackGroup("id", logger, 2) 565 callbacks.Register("c1", callback1) 566 callbacks.Register("c2", callback2) 567 ctrl3 := callbacks.Register("c3", callback3) 568 ctrl4 := callbacks.Register("c4", callback4) 569 570 go func() { 571 chStarted <- struct{}{} 572 start := time.Now() 573 executed = callbacks.CycleCallback(shouldNotAbort) 574 d = time.Since(start) 575 chFinished <- struct{}{} 576 }() 577 <-chStarted 578 time.Sleep(25 * time.Millisecond) 579 require.Nil(t, ctrl3.Unregister(ctx)) 580 require.Nil(t, ctrl4.Unregister(ctx)) 581 <-chFinished 582 583 assert.True(t, executed) 584 assert.Equal(t, 1, executedCounter1) 585 assert.Equal(t, 1, executedCounter2) 586 assert.Equal(t, 0, executedCounter3) 587 assert.Equal(t, 0, executedCounter3) 588 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 589 }) 590 591 t.Run("unregister while running", func(t *testing.T) { 592 counter1 := 0 593 counter2 := 0 594 max := 25 595 596 callback1 := func(shouldAbort ShouldAbortCallback) bool { 597 for { 598 if shouldAbort() { 599 return false 600 } 601 602 time.Sleep(10 * time.Millisecond) 603 counter1++ 604 605 // 10ms * 25 = 250ms 606 if counter1 > max { 607 return true 608 } 609 } 610 } 611 callback2 := func(shouldAbort ShouldAbortCallback) bool { 612 for { 613 if shouldAbort() { 614 return false 615 } 616 617 time.Sleep(10 * time.Millisecond) 618 counter2++ 619 620 // 10ms * 25 = 250ms 621 if counter2 > max { 622 return true 623 } 624 } 625 } 626 627 chStarted := make(chan struct{}, 1) 628 chFinished := make(chan struct{}, 1) 629 var executed bool 630 var d time.Duration 631 632 callbacks := NewCallbackGroup("id", logger, 2) 633 ctrl1 := callbacks.Register("c1", callback1) 634 ctrl2 := callbacks.Register("c2", callback2) 635 636 go func() { 637 chStarted <- struct{}{} 638 start := time.Now() 639 executed = callbacks.CycleCallback(shouldNotAbort) 640 d = time.Since(start) 641 chFinished <- struct{}{} 642 }() 643 <-chStarted 644 time.Sleep(50 * time.Millisecond) 645 require.NoError(t, ctrl1.Unregister(ctx)) 646 require.NoError(t, ctrl2.Unregister(ctx)) 647 <-chFinished 648 649 assert.False(t, executed) 650 assert.LessOrEqual(t, counter1, max) 651 assert.LessOrEqual(t, counter2, max) 652 assert.LessOrEqual(t, d, 200*time.Millisecond) 653 }) 654 } 655 656 func TestCycleCallback_Parallel_Deactivate(t *testing.T) { 657 ctx := context.Background() 658 logger, _ := test.NewNullLogger() 659 shouldNotAbort := func() bool { return false } 660 661 t.Run("1 executable callback, 1 deactivated", func(t *testing.T) { 662 executedCounter := 0 663 callback := func(shouldAbort ShouldAbortCallback) bool { 664 time.Sleep(50 * time.Millisecond) 665 executedCounter++ 666 return true 667 } 668 var executed bool 669 var d time.Duration 670 671 callbacks := NewCallbackGroup("id", logger, 2) 672 ctrl := callbacks.Register("c1", callback) 673 require.Nil(t, ctrl.Deactivate(ctx)) 674 675 start := time.Now() 676 executed = callbacks.CycleCallback(shouldNotAbort) 677 d = time.Since(start) 678 679 assert.False(t, executed) 680 assert.Equal(t, 0, executedCounter) 681 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 682 }) 683 684 t.Run("2 executable callbacks, 2 deactivated", func(t *testing.T) { 685 executedCounter1 := 0 686 callback1 := func(shouldAbort ShouldAbortCallback) bool { 687 time.Sleep(50 * time.Millisecond) 688 executedCounter1++ 689 return true 690 } 691 executedCounter2 := 0 692 callback2 := func(shouldAbort ShouldAbortCallback) bool { 693 time.Sleep(25 * time.Millisecond) 694 executedCounter2++ 695 return true 696 } 697 var executed bool 698 var d time.Duration 699 700 callbacks := NewCallbackGroup("id", logger, 2) 701 ctrl1 := callbacks.Register("c1", callback1) 702 ctrl2 := callbacks.Register("c2", callback2) 703 require.Nil(t, ctrl1.Deactivate(ctx)) 704 require.Nil(t, ctrl2.Deactivate(ctx)) 705 706 start := time.Now() 707 executed = callbacks.CycleCallback(shouldNotAbort) 708 d = time.Since(start) 709 710 assert.False(t, executed) 711 assert.Equal(t, 0, executedCounter1) 712 assert.Equal(t, 0, executedCounter2) 713 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 714 }) 715 716 t.Run("2 executable callbacks, 1 deactivated", func(t *testing.T) { 717 executedCounter1 := 0 718 callback1 := func(shouldAbort ShouldAbortCallback) bool { 719 time.Sleep(50 * time.Millisecond) 720 executedCounter1++ 721 return true 722 } 723 executedCounter2 := 0 724 callback2 := func(shouldAbort ShouldAbortCallback) bool { 725 time.Sleep(25 * time.Millisecond) 726 executedCounter2++ 727 return true 728 } 729 var executed bool 730 var d time.Duration 731 732 callbacks := NewCallbackGroup("id", logger, 2) 733 ctrl1 := callbacks.Register("c1", callback1) 734 callbacks.Register("c2", callback2) 735 require.Nil(t, ctrl1.Deactivate(ctx)) 736 737 start := time.Now() 738 executed = callbacks.CycleCallback(shouldNotAbort) 739 d = time.Since(start) 740 741 assert.True(t, executed) 742 assert.Equal(t, 0, executedCounter1) 743 assert.Equal(t, 1, executedCounter2) 744 assert.GreaterOrEqual(t, d, 25*time.Millisecond) 745 }) 746 747 t.Run("4 executable callbacks, all deactivated at different time", func(t *testing.T) { 748 executedCounter1 := 0 749 callback1 := func(shouldAbort ShouldAbortCallback) bool { 750 time.Sleep(25 * time.Millisecond) 751 executedCounter1++ 752 return true 753 } 754 executedCounter2 := 0 755 callback2 := func(shouldAbort ShouldAbortCallback) bool { 756 time.Sleep(25 * time.Millisecond) 757 executedCounter2++ 758 return true 759 } 760 executedCounter3 := 0 761 callback3 := func(shouldAbort ShouldAbortCallback) bool { 762 time.Sleep(25 * time.Millisecond) 763 executedCounter3++ 764 return true 765 } 766 executedCounter4 := 0 767 callback4 := func(shouldAbort ShouldAbortCallback) bool { 768 time.Sleep(25 * time.Millisecond) 769 executedCounter4++ 770 return true 771 } 772 var executed1 bool 773 var executed2 bool 774 var executed3 bool 775 var executed4 bool 776 var d1 time.Duration 777 var d2 time.Duration 778 var d3 time.Duration 779 var d4 time.Duration 780 781 callbacks := NewCallbackGroup("id", logger, 2) 782 ctrl1 := callbacks.Register("c1", callback1) 783 ctrl2 := callbacks.Register("c2", callback2) 784 ctrl3 := callbacks.Register("c3", callback3) 785 ctrl4 := callbacks.Register("c4", callback4) 786 require.Nil(t, ctrl3.Deactivate(ctx)) 787 788 start := time.Now() 789 executed1 = callbacks.CycleCallback(shouldNotAbort) 790 d1 = time.Since(start) 791 792 require.Nil(t, ctrl1.Deactivate(ctx)) 793 794 start = time.Now() 795 executed2 = callbacks.CycleCallback(shouldNotAbort) 796 d2 = time.Since(start) 797 798 require.Nil(t, ctrl4.Deactivate(ctx)) 799 800 start = time.Now() 801 executed3 = callbacks.CycleCallback(shouldNotAbort) 802 d3 = time.Since(start) 803 804 require.Nil(t, ctrl2.Deactivate(ctx)) 805 806 start = time.Now() 807 executed4 = callbacks.CycleCallback(shouldNotAbort) 808 d4 = time.Since(start) 809 810 assert.True(t, executed1) 811 assert.True(t, executed2) 812 assert.True(t, executed3) 813 assert.False(t, executed4) 814 assert.Equal(t, 1, executedCounter1) 815 assert.Equal(t, 3, executedCounter2) 816 assert.Equal(t, 0, executedCounter3) 817 assert.Equal(t, 2, executedCounter4) 818 assert.GreaterOrEqual(t, d1, 50*time.Millisecond) 819 assert.GreaterOrEqual(t, d2, 25*time.Millisecond) 820 assert.GreaterOrEqual(t, d3, 25*time.Millisecond) 821 assert.GreaterOrEqual(t, d4, 0*time.Millisecond) 822 }) 823 824 t.Run("deactivate is waiting till the end of execution", func(t *testing.T) { 825 executedCounter := 0 826 callback := func(shouldAbort ShouldAbortCallback) bool { 827 time.Sleep(50 * time.Millisecond) 828 executedCounter++ 829 return true 830 } 831 chStarted := make(chan struct{}, 1) 832 chFinished := make(chan struct{}, 1) 833 var executed bool 834 var d time.Duration 835 836 callbacks := NewCallbackGroup("id", logger, 2) 837 ctrl := callbacks.Register("c", callback) 838 839 go func() { 840 chStarted <- struct{}{} 841 start := time.Now() 842 executed = callbacks.CycleCallback(shouldNotAbort) 843 d = time.Since(start) 844 chFinished <- struct{}{} 845 }() 846 <-chStarted 847 start := time.Now() 848 time.Sleep(25 * time.Millisecond) 849 require.Nil(t, ctrl.Deactivate(ctx)) 850 du := time.Since(start) 851 <-chFinished 852 853 assert.True(t, executed) 854 assert.Equal(t, 1, executedCounter) 855 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 856 assert.GreaterOrEqual(t, du, 40*time.Millisecond) 857 }) 858 859 t.Run("deactivate fails due to context timeout", func(t *testing.T) { 860 executedCounter := 0 861 callback := func(shouldAbort ShouldAbortCallback) bool { 862 time.Sleep(50 * time.Millisecond) 863 executedCounter++ 864 return true 865 } 866 chStarted := make(chan struct{}, 1) 867 chFinished := make(chan struct{}, 1) 868 var executed1 bool 869 var executed2 bool 870 var d1 time.Duration 871 var d2 time.Duration 872 873 callbacks := NewCallbackGroup("id", logger, 2) 874 ctrl := callbacks.Register("c", callback) 875 876 go func() { 877 chStarted <- struct{}{} 878 start := time.Now() 879 executed1 = callbacks.CycleCallback(shouldNotAbort) 880 d1 = time.Since(start) 881 chFinished <- struct{}{} 882 }() 883 <-chStarted 884 start := time.Now() 885 time.Sleep(25 * time.Millisecond) 886 ctxTimeout, cancel := context.WithTimeout(ctx, 5*time.Millisecond) 887 defer cancel() 888 require.NotNil(t, ctrl.Deactivate(ctxTimeout)) 889 du := time.Since(start) 890 <-chFinished 891 892 go func() { 893 start := time.Now() 894 executed2 = callbacks.CycleCallback(shouldNotAbort) 895 d2 = time.Since(start) 896 chFinished <- struct{}{} 897 }() 898 <-chFinished 899 900 assert.True(t, executed1) 901 assert.True(t, executed2) 902 assert.Equal(t, 2, executedCounter) 903 assert.GreaterOrEqual(t, d1, 50*time.Millisecond) 904 assert.GreaterOrEqual(t, d2, 50*time.Millisecond) 905 assert.GreaterOrEqual(t, du, 30*time.Millisecond) 906 }) 907 908 t.Run("deactivate 3rd and 4th while executing", func(t *testing.T) { 909 executedCounter1 := 0 910 callback1 := func(shouldAbort ShouldAbortCallback) bool { 911 time.Sleep(50 * time.Millisecond) 912 executedCounter1++ 913 return true 914 } 915 executedCounter2 := 0 916 callback2 := func(shouldAbort ShouldAbortCallback) bool { 917 time.Sleep(50 * time.Millisecond) 918 executedCounter2++ 919 return true 920 } 921 executedCounter3 := 0 922 callback3 := func(shouldAbort ShouldAbortCallback) bool { 923 time.Sleep(50 * time.Millisecond) 924 executedCounter3++ 925 return true 926 } 927 executedCounter4 := 0 928 callback4 := func(shouldAbort ShouldAbortCallback) bool { 929 time.Sleep(50 * time.Millisecond) 930 executedCounter4++ 931 return true 932 } 933 chStarted := make(chan struct{}, 1) 934 chFinished := make(chan struct{}, 1) 935 var executed bool 936 var d time.Duration 937 938 callbacks := NewCallbackGroup("id", logger, 2) 939 callbacks.Register("c1", callback1) 940 callbacks.Register("c2", callback2) 941 ctrl3 := callbacks.Register("c3", callback3) 942 ctrl4 := callbacks.Register("c4", callback4) 943 944 go func() { 945 chStarted <- struct{}{} 946 start := time.Now() 947 executed = callbacks.CycleCallback(shouldNotAbort) 948 d = time.Since(start) 949 chFinished <- struct{}{} 950 }() 951 <-chStarted 952 time.Sleep(25 * time.Millisecond) 953 require.Nil(t, ctrl3.Deactivate(ctx)) 954 require.Nil(t, ctrl4.Deactivate(ctx)) 955 <-chFinished 956 957 assert.True(t, executed) 958 assert.Equal(t, 1, executedCounter1) 959 assert.Equal(t, 1, executedCounter2) 960 assert.Equal(t, 0, executedCounter3) 961 assert.Equal(t, 0, executedCounter3) 962 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 963 }) 964 965 t.Run("deactivate while running", func(t *testing.T) { 966 counter1 := 0 967 counter2 := 0 968 max := 25 969 970 callback1 := func(shouldAbort ShouldAbortCallback) bool { 971 for { 972 if shouldAbort() { 973 return false 974 } 975 976 time.Sleep(10 * time.Millisecond) 977 counter1++ 978 979 // 10ms * 25 = 250ms 980 if counter1 > max { 981 return true 982 } 983 } 984 } 985 callback2 := func(shouldAbort ShouldAbortCallback) bool { 986 for { 987 if shouldAbort() { 988 return false 989 } 990 991 time.Sleep(10 * time.Millisecond) 992 counter2++ 993 994 // 10ms * 25 = 250ms 995 if counter2 > max { 996 return true 997 } 998 } 999 } 1000 1001 chStarted := make(chan struct{}, 1) 1002 chFinished := make(chan struct{}, 1) 1003 var executed bool 1004 var d time.Duration 1005 1006 callbacks := NewCallbackGroup("id", logger, 2) 1007 ctrl1 := callbacks.Register("c1", callback1) 1008 ctrl2 := callbacks.Register("c2", callback2) 1009 1010 go func() { 1011 chStarted <- struct{}{} 1012 start := time.Now() 1013 executed = callbacks.CycleCallback(shouldNotAbort) 1014 d = time.Since(start) 1015 chFinished <- struct{}{} 1016 }() 1017 <-chStarted 1018 time.Sleep(50 * time.Millisecond) 1019 require.NoError(t, ctrl1.Deactivate(ctx)) 1020 require.NoError(t, ctrl2.Deactivate(ctx)) 1021 <-chFinished 1022 1023 assert.False(t, executed) 1024 assert.LessOrEqual(t, counter1, max) 1025 assert.LessOrEqual(t, counter2, max) 1026 assert.LessOrEqual(t, d, 200*time.Millisecond) 1027 1028 t.Run("does not abort after activated back again", func(t *testing.T) { 1029 require.NoError(t, ctrl1.Activate()) 1030 require.NoError(t, ctrl2.Activate()) 1031 1032 counter1 = 0 1033 counter2 = 0 1034 max = 10 1035 1036 go func() { 1037 start := time.Now() 1038 executed = callbacks.CycleCallback(shouldNotAbort) 1039 d = time.Since(start) 1040 chFinished <- struct{}{} 1041 }() 1042 <-chFinished 1043 1044 assert.True(t, executed) 1045 assert.Greater(t, counter1, max) 1046 assert.Greater(t, counter2, max) 1047 assert.GreaterOrEqual(t, d, 100*time.Millisecond) 1048 }) 1049 }) 1050 } 1051 1052 func TestCycleCallback_Sequential(t *testing.T) { 1053 logger, _ := test.NewNullLogger() 1054 shouldNotAbort := func() bool { return false } 1055 1056 t.Run("no callbacks", func(t *testing.T) { 1057 var executed bool 1058 1059 callbacks := NewCallbackGroup("id", logger, 1) 1060 1061 executed = callbacks.CycleCallback(shouldNotAbort) 1062 1063 assert.False(t, executed) 1064 }) 1065 1066 t.Run("2 executable callbacks", func(t *testing.T) { 1067 executedCounter1 := 0 1068 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1069 time.Sleep(50 * time.Millisecond) 1070 executedCounter1++ 1071 return true 1072 } 1073 executedCounter2 := 0 1074 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1075 time.Sleep(25 * time.Millisecond) 1076 executedCounter2++ 1077 return true 1078 } 1079 var executed bool 1080 var d time.Duration 1081 1082 callbacks := NewCallbackGroup("id", logger, 1) 1083 callbacks.Register("c1", callback1) 1084 callbacks.Register("c2", callback2) 1085 1086 start := time.Now() 1087 executed = callbacks.CycleCallback(shouldNotAbort) 1088 d = time.Since(start) 1089 1090 assert.True(t, executed) 1091 assert.Equal(t, 1, executedCounter1) 1092 assert.Equal(t, 1, executedCounter2) 1093 assert.GreaterOrEqual(t, d, 75*time.Millisecond) 1094 }) 1095 1096 t.Run("2 non-executable callbacks", func(t *testing.T) { 1097 executedCounter1 := 0 1098 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1099 time.Sleep(10 * time.Millisecond) 1100 executedCounter1++ 1101 return false 1102 } 1103 executedCounter2 := 0 1104 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1105 time.Sleep(10 * time.Millisecond) 1106 executedCounter2++ 1107 return false 1108 } 1109 var executed bool 1110 var d time.Duration 1111 1112 callbacks := NewCallbackGroup("id", logger, 1) 1113 callbacks.Register("c1", callback1) 1114 callbacks.Register("c2", callback2) 1115 1116 start := time.Now() 1117 executed = callbacks.CycleCallback(shouldNotAbort) 1118 d = time.Since(start) 1119 1120 assert.False(t, executed) 1121 assert.Equal(t, 1, executedCounter1) 1122 assert.Equal(t, 1, executedCounter2) 1123 assert.GreaterOrEqual(t, d, 10*time.Millisecond) 1124 }) 1125 1126 t.Run("2 executable callbacks, not executed due to should abort", func(t *testing.T) { 1127 executedCounter1 := 0 1128 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1129 time.Sleep(25 * time.Millisecond) 1130 executedCounter1++ 1131 return true 1132 } 1133 executedCounter2 := 0 1134 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1135 time.Sleep(25 * time.Millisecond) 1136 executedCounter2++ 1137 return true 1138 } 1139 shouldAbortCounter := 0 1140 shouldAbort := func() bool { 1141 shouldAbortCounter++ 1142 return shouldAbortCounter > 1 1143 } 1144 1145 var executed bool 1146 var d time.Duration 1147 1148 callbacks := NewCallbackGroup("id", logger, 1) 1149 callbacks.Register("c1", callback1) 1150 callbacks.Register("c2", callback2) 1151 1152 start := time.Now() 1153 executed = callbacks.CycleCallback(shouldAbort) 1154 d = time.Since(start) 1155 1156 assert.True(t, executed) 1157 assert.Equal(t, 1, executedCounter1) 1158 assert.Equal(t, 0, executedCounter2) 1159 assert.GreaterOrEqual(t, d, 25*time.Millisecond) 1160 }) 1161 1162 t.Run("register new while executing", func(t *testing.T) { 1163 executedCounter1 := 0 1164 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1165 time.Sleep(50 * time.Millisecond) 1166 executedCounter1++ 1167 return true 1168 } 1169 executedCounter2 := 0 1170 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1171 time.Sleep(50 * time.Millisecond) 1172 executedCounter2++ 1173 return true 1174 } 1175 chStarted := make(chan struct{}, 1) 1176 chFinished := make(chan struct{}, 1) 1177 var executed bool 1178 var d time.Duration 1179 1180 callbacks := NewCallbackGroup("id", logger, 1) 1181 callbacks.Register("c1", callback1) 1182 1183 go func() { 1184 chStarted <- struct{}{} 1185 start := time.Now() 1186 executed = callbacks.CycleCallback(shouldNotAbort) 1187 d = time.Since(start) 1188 chFinished <- struct{}{} 1189 }() 1190 <-chStarted 1191 time.Sleep(25 * time.Millisecond) 1192 callbacks.Register("c2", callback2) 1193 <-chFinished 1194 1195 assert.True(t, executed) 1196 assert.Equal(t, 1, executedCounter1) 1197 assert.Equal(t, 1, executedCounter2) 1198 assert.GreaterOrEqual(t, d, 100*time.Millisecond) 1199 }) 1200 1201 t.Run("run with intervals", func(T *testing.T) { 1202 ticker := NewFixedTicker(10 * time.Millisecond) 1203 intervals2 := NewSeriesIntervals([]time.Duration{ 1204 10 * time.Millisecond, 30 * time.Millisecond, 50 * time.Millisecond, 1205 }) 1206 intervals3 := NewFixedIntervals(60 * time.Millisecond) 1207 now := time.Now() 1208 1209 executionTimes1 := []time.Duration{} 1210 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1211 executionTimes1 = append(executionTimes1, time.Since(now)) 1212 return true 1213 } 1214 executionCounter2 := 0 1215 executionTimes2 := []time.Duration{} 1216 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1217 executionCounter2++ 1218 executionTimes2 = append(executionTimes2, time.Since(now)) 1219 // reports executed every 3 calls, should result in 10, 30, 50, 50, 10, 30, 50, 50, ... intervals 1220 return executionCounter2%4 == 0 1221 } 1222 executionTimes3 := []time.Duration{} 1223 callback3 := func(shouldAbort ShouldAbortCallback) bool { 1224 executionTimes3 = append(executionTimes3, time.Since(now)) 1225 return true 1226 } 1227 1228 callbacks := NewCallbackGroup("id", logger, 1) 1229 // should be called on every tick, with 10 intervals 1230 callbacks.Register("c1", callback1) 1231 // should be called with 10, 30, 50, 50, 10, 30, 50, 50, ... intervals 1232 callbacks.Register("c2", callback2, WithIntervals(intervals2)) 1233 // should be called with 60, 60, ... intervals 1234 callbacks.Register("c3", callback3, WithIntervals(intervals3)) 1235 1236 cm := NewManager(ticker, callbacks.CycleCallback, logger) 1237 cm.Start() 1238 time.Sleep(400 * time.Millisecond) 1239 cm.StopAndWait(context.Background()) 1240 1241 // within 400 ms c1 should be called at least 30x 1242 require.GreaterOrEqual(t, len(executionTimes1), 30) 1243 // 1st call on 1st tick after 10ms 1244 sumDuration := time.Duration(10) 1245 for i := 0; i < 30; i++ { 1246 assert.GreaterOrEqual(t, executionTimes1[i], sumDuration) 1247 sumDuration += 10 * time.Millisecond 1248 } 1249 1250 // within 400 ms c2 should be called at least 8x 1251 require.GreaterOrEqual(t, len(executionTimes2), 8) 1252 // 1st call on 1st tick after 10ms 1253 sumDuration = time.Duration(0) 1254 for i := 0; i < 8; i++ { 1255 assert.GreaterOrEqual(t, executionTimes2[i], sumDuration) 1256 switch (i + 1) % 4 { 1257 case 0: 1258 sumDuration += 10 * time.Millisecond 1259 case 1: 1260 sumDuration += 30 * time.Millisecond 1261 case 2, 3: 1262 sumDuration += 50 * time.Millisecond 1263 } 1264 } 1265 1266 // within 400 ms c3 should be called at least 6x 1267 require.GreaterOrEqual(t, len(executionTimes3), 6) 1268 // 1st call on 1st tick after 10ms 1269 sumDuration = time.Duration(0) 1270 for i := 0; i < 6; i++ { 1271 assert.GreaterOrEqual(t, executionTimes3[i], sumDuration) 1272 sumDuration += 60 * time.Millisecond 1273 } 1274 }) 1275 } 1276 1277 func TestCycleCallback_Sequential_Unregister(t *testing.T) { 1278 ctx := context.Background() 1279 logger, _ := test.NewNullLogger() 1280 shouldNotAbort := func() bool { return false } 1281 1282 t.Run("1 executable callback, 1 unregistered", func(t *testing.T) { 1283 executedCounter := 0 1284 callback := func(shouldAbort ShouldAbortCallback) bool { 1285 time.Sleep(50 * time.Millisecond) 1286 executedCounter++ 1287 return true 1288 } 1289 var executed bool 1290 var d time.Duration 1291 1292 callbacks := NewCallbackGroup("id", logger, 1) 1293 ctrl := callbacks.Register("c1", callback) 1294 require.Nil(t, ctrl.Unregister(ctx)) 1295 1296 start := time.Now() 1297 executed = callbacks.CycleCallback(shouldNotAbort) 1298 d = time.Since(start) 1299 1300 assert.False(t, executed) 1301 assert.Equal(t, 0, executedCounter) 1302 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 1303 }) 1304 1305 t.Run("2 executable callbacks, 2 unregistered", func(t *testing.T) { 1306 executedCounter1 := 0 1307 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1308 time.Sleep(50 * time.Millisecond) 1309 executedCounter1++ 1310 return true 1311 } 1312 executedCounter2 := 0 1313 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1314 time.Sleep(25 * time.Millisecond) 1315 executedCounter2++ 1316 return true 1317 } 1318 var executed bool 1319 var d time.Duration 1320 1321 callbacks := NewCallbackGroup("id", logger, 1) 1322 ctrl1 := callbacks.Register("c1", callback1) 1323 ctrl2 := callbacks.Register("c2", callback2) 1324 require.Nil(t, ctrl1.Unregister(ctx)) 1325 require.Nil(t, ctrl2.Unregister(ctx)) 1326 1327 start := time.Now() 1328 executed = callbacks.CycleCallback(shouldNotAbort) 1329 d = time.Since(start) 1330 1331 assert.False(t, executed) 1332 assert.Equal(t, 0, executedCounter1) 1333 assert.Equal(t, 0, executedCounter2) 1334 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 1335 }) 1336 1337 t.Run("2 executable callbacks, 1 unregistered", func(t *testing.T) { 1338 executedCounter1 := 0 1339 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1340 time.Sleep(50 * time.Millisecond) 1341 executedCounter1++ 1342 return true 1343 } 1344 executedCounter2 := 0 1345 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1346 time.Sleep(25 * time.Millisecond) 1347 executedCounter2++ 1348 return true 1349 } 1350 var executed bool 1351 var d time.Duration 1352 1353 callbacks := NewCallbackGroup("id", logger, 1) 1354 ctrl1 := callbacks.Register("c1", callback1) 1355 callbacks.Register("c2", callback2) 1356 require.Nil(t, ctrl1.Unregister(ctx)) 1357 1358 start := time.Now() 1359 executed = callbacks.CycleCallback(shouldNotAbort) 1360 d = time.Since(start) 1361 1362 assert.True(t, executed) 1363 assert.Equal(t, 0, executedCounter1) 1364 assert.Equal(t, 1, executedCounter2) 1365 assert.GreaterOrEqual(t, d, 25*time.Millisecond) 1366 }) 1367 1368 t.Run("4 executable callbacks, all unregistered at different time", func(t *testing.T) { 1369 executedCounter1 := 0 1370 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1371 time.Sleep(25 * time.Millisecond) 1372 executedCounter1++ 1373 return true 1374 } 1375 executedCounter2 := 0 1376 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1377 time.Sleep(25 * time.Millisecond) 1378 executedCounter2++ 1379 return true 1380 } 1381 executedCounter3 := 0 1382 callback3 := func(shouldAbort ShouldAbortCallback) bool { 1383 time.Sleep(25 * time.Millisecond) 1384 executedCounter3++ 1385 return true 1386 } 1387 executedCounter4 := 0 1388 callback4 := func(shouldAbort ShouldAbortCallback) bool { 1389 time.Sleep(25 * time.Millisecond) 1390 executedCounter4++ 1391 return true 1392 } 1393 var executed1 bool 1394 var executed2 bool 1395 var executed3 bool 1396 var executed4 bool 1397 var d1 time.Duration 1398 var d2 time.Duration 1399 var d3 time.Duration 1400 var d4 time.Duration 1401 1402 callbacks := NewCallbackGroup("id", logger, 1) 1403 ctrl1 := callbacks.Register("c1", callback1) 1404 ctrl2 := callbacks.Register("c2", callback2) 1405 ctrl3 := callbacks.Register("c3", callback3) 1406 ctrl4 := callbacks.Register("c4", callback4) 1407 require.Nil(t, ctrl3.Unregister(ctx)) 1408 1409 start := time.Now() 1410 executed1 = callbacks.CycleCallback(shouldNotAbort) 1411 d1 = time.Since(start) 1412 1413 require.Nil(t, ctrl1.Unregister(ctx)) 1414 1415 start = time.Now() 1416 executed2 = callbacks.CycleCallback(shouldNotAbort) 1417 d2 = time.Since(start) 1418 1419 require.Nil(t, ctrl4.Unregister(ctx)) 1420 1421 start = time.Now() 1422 executed3 = callbacks.CycleCallback(shouldNotAbort) 1423 d3 = time.Since(start) 1424 1425 require.Nil(t, ctrl2.Unregister(ctx)) 1426 1427 start = time.Now() 1428 executed4 = callbacks.CycleCallback(shouldNotAbort) 1429 d4 = time.Since(start) 1430 1431 assert.True(t, executed1) 1432 assert.True(t, executed2) 1433 assert.True(t, executed3) 1434 assert.False(t, executed4) 1435 assert.Equal(t, 1, executedCounter1) 1436 assert.Equal(t, 3, executedCounter2) 1437 assert.Equal(t, 0, executedCounter3) 1438 assert.Equal(t, 2, executedCounter4) 1439 assert.GreaterOrEqual(t, d1, 75*time.Millisecond) 1440 assert.GreaterOrEqual(t, d2, 50*time.Millisecond) 1441 assert.GreaterOrEqual(t, d3, 25*time.Millisecond) 1442 assert.GreaterOrEqual(t, d4, 0*time.Millisecond) 1443 }) 1444 1445 t.Run("unregister is waiting till the end of execution", func(t *testing.T) { 1446 executedCounter := 0 1447 callback := func(shouldAbort ShouldAbortCallback) bool { 1448 time.Sleep(50 * time.Millisecond) 1449 executedCounter++ 1450 return true 1451 } 1452 chStarted := make(chan struct{}, 1) 1453 chFinished := make(chan struct{}, 1) 1454 var executed bool 1455 var d time.Duration 1456 1457 callbacks := NewCallbackGroup("id", logger, 1) 1458 ctrl := callbacks.Register("c", callback) 1459 1460 go func() { 1461 chStarted <- struct{}{} 1462 start := time.Now() 1463 executed = callbacks.CycleCallback(shouldNotAbort) 1464 d = time.Since(start) 1465 chFinished <- struct{}{} 1466 }() 1467 <-chStarted 1468 start := time.Now() 1469 time.Sleep(25 * time.Millisecond) 1470 require.Nil(t, ctrl.Unregister(ctx)) 1471 du := time.Since(start) 1472 <-chFinished 1473 1474 assert.True(t, executed) 1475 assert.Equal(t, 1, executedCounter) 1476 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 1477 assert.GreaterOrEqual(t, du, 40*time.Millisecond) 1478 }) 1479 1480 t.Run("unregister fails due to context timeout", func(t *testing.T) { 1481 executedCounter := 0 1482 callback := func(shouldAbort ShouldAbortCallback) bool { 1483 time.Sleep(50 * time.Millisecond) 1484 executedCounter++ 1485 return true 1486 } 1487 chStarted := make(chan struct{}, 1) 1488 chFinished := make(chan struct{}, 1) 1489 var executed1 bool 1490 var executed2 bool 1491 var d1 time.Duration 1492 var d2 time.Duration 1493 1494 callbacks := NewCallbackGroup("id", logger, 1) 1495 ctrl := callbacks.Register("c", callback) 1496 1497 go func() { 1498 chStarted <- struct{}{} 1499 start := time.Now() 1500 executed1 = callbacks.CycleCallback(shouldNotAbort) 1501 d1 = time.Since(start) 1502 chFinished <- struct{}{} 1503 }() 1504 <-chStarted 1505 start := time.Now() 1506 time.Sleep(25 * time.Millisecond) 1507 ctxTimeout, cancel := context.WithTimeout(ctx, 5*time.Millisecond) 1508 defer cancel() 1509 require.NotNil(t, ctrl.Unregister(ctxTimeout)) 1510 du := time.Since(start) 1511 <-chFinished 1512 1513 go func() { 1514 start := time.Now() 1515 executed2 = callbacks.CycleCallback(shouldNotAbort) 1516 d2 = time.Since(start) 1517 chFinished <- struct{}{} 1518 }() 1519 <-chFinished 1520 1521 assert.True(t, executed1) 1522 assert.True(t, executed2) 1523 assert.Equal(t, 2, executedCounter) 1524 assert.GreaterOrEqual(t, d1, 50*time.Millisecond) 1525 assert.GreaterOrEqual(t, d2, 50*time.Millisecond) 1526 assert.GreaterOrEqual(t, du, 30*time.Millisecond) 1527 }) 1528 1529 t.Run("unregister 2nd and 3rd while executing", func(t *testing.T) { 1530 executedCounter1 := 0 1531 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1532 time.Sleep(50 * time.Millisecond) 1533 executedCounter1++ 1534 return true 1535 } 1536 executedCounter2 := 0 1537 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1538 time.Sleep(50 * time.Millisecond) 1539 executedCounter2++ 1540 return true 1541 } 1542 executedCounter3 := 0 1543 callback3 := func(shouldAbort ShouldAbortCallback) bool { 1544 time.Sleep(50 * time.Millisecond) 1545 executedCounter3++ 1546 return true 1547 } 1548 chStarted := make(chan struct{}, 1) 1549 chFinished := make(chan struct{}, 1) 1550 var executed bool 1551 var d time.Duration 1552 1553 callbacks := NewCallbackGroup("id", logger, 1) 1554 callbacks.Register("c1", callback1) 1555 ctrl2 := callbacks.Register("c2", callback2) 1556 ctrl3 := callbacks.Register("c3", callback3) 1557 1558 go func() { 1559 chStarted <- struct{}{} 1560 start := time.Now() 1561 executed = callbacks.CycleCallback(shouldNotAbort) 1562 d = time.Since(start) 1563 chFinished <- struct{}{} 1564 }() 1565 <-chStarted 1566 time.Sleep(25 * time.Millisecond) 1567 require.Nil(t, ctrl2.Unregister(ctx)) 1568 require.Nil(t, ctrl3.Unregister(ctx)) 1569 <-chFinished 1570 1571 assert.True(t, executed) 1572 assert.Equal(t, 1, executedCounter1) 1573 assert.Equal(t, 0, executedCounter2) 1574 assert.Equal(t, 0, executedCounter3) 1575 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 1576 }) 1577 1578 t.Run("unregister while running", func(t *testing.T) { 1579 counter := 0 1580 max := 25 1581 callback := func(shouldAbort ShouldAbortCallback) bool { 1582 for { 1583 if shouldAbort() { 1584 return false 1585 } 1586 1587 time.Sleep(10 * time.Millisecond) 1588 counter++ 1589 1590 // 10ms * 25 = 250ms 1591 if counter > max { 1592 return true 1593 } 1594 } 1595 } 1596 chStarted := make(chan struct{}, 1) 1597 chFinished := make(chan struct{}, 1) 1598 var executed bool 1599 var d time.Duration 1600 1601 callbacks := NewCallbackGroup("id", logger, 1) 1602 ctrl := callbacks.Register("c", callback) 1603 1604 go func() { 1605 chStarted <- struct{}{} 1606 start := time.Now() 1607 executed = callbacks.CycleCallback(shouldNotAbort) 1608 d = time.Since(start) 1609 chFinished <- struct{}{} 1610 }() 1611 <-chStarted 1612 time.Sleep(50 * time.Millisecond) 1613 require.NoError(t, ctrl.Unregister(ctx)) 1614 <-chFinished 1615 1616 assert.False(t, executed) 1617 assert.LessOrEqual(t, counter, max) 1618 assert.LessOrEqual(t, d, 200*time.Millisecond) 1619 }) 1620 } 1621 1622 func TestCycleCallback_Sequential_Deactivate(t *testing.T) { 1623 ctx := context.Background() 1624 logger, _ := test.NewNullLogger() 1625 shouldNotAbort := func() bool { return false } 1626 1627 t.Run("1 executable callback, 1 deactivated", func(t *testing.T) { 1628 executedCounter := 0 1629 callback := func(shouldAbort ShouldAbortCallback) bool { 1630 time.Sleep(50 * time.Millisecond) 1631 executedCounter++ 1632 return true 1633 } 1634 var executed bool 1635 var d time.Duration 1636 1637 callbacks := NewCallbackGroup("id", logger, 1) 1638 ctrl := callbacks.Register("c1", callback) 1639 require.Nil(t, ctrl.Deactivate(ctx)) 1640 1641 start := time.Now() 1642 executed = callbacks.CycleCallback(shouldNotAbort) 1643 d = time.Since(start) 1644 1645 assert.False(t, executed) 1646 assert.Equal(t, 0, executedCounter) 1647 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 1648 }) 1649 1650 t.Run("2 executable callbacks, 2 deactivated", func(t *testing.T) { 1651 executedCounter1 := 0 1652 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1653 time.Sleep(50 * time.Millisecond) 1654 executedCounter1++ 1655 return true 1656 } 1657 executedCounter2 := 0 1658 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1659 time.Sleep(25 * time.Millisecond) 1660 executedCounter2++ 1661 return true 1662 } 1663 var executed bool 1664 var d time.Duration 1665 1666 callbacks := NewCallbackGroup("id", logger, 1) 1667 ctrl1 := callbacks.Register("c1", callback1) 1668 ctrl2 := callbacks.Register("c2", callback2) 1669 require.Nil(t, ctrl1.Deactivate(ctx)) 1670 require.Nil(t, ctrl2.Deactivate(ctx)) 1671 1672 start := time.Now() 1673 executed = callbacks.CycleCallback(shouldNotAbort) 1674 d = time.Since(start) 1675 1676 assert.False(t, executed) 1677 assert.Equal(t, 0, executedCounter1) 1678 assert.Equal(t, 0, executedCounter2) 1679 assert.GreaterOrEqual(t, d, 0*time.Millisecond) 1680 }) 1681 1682 t.Run("2 executable callbacks, 1 deactivated", func(t *testing.T) { 1683 executedCounter1 := 0 1684 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1685 time.Sleep(50 * time.Millisecond) 1686 executedCounter1++ 1687 return true 1688 } 1689 executedCounter2 := 0 1690 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1691 time.Sleep(25 * time.Millisecond) 1692 executedCounter2++ 1693 return true 1694 } 1695 var executed bool 1696 var d time.Duration 1697 1698 callbacks := NewCallbackGroup("id", logger, 1) 1699 ctrl1 := callbacks.Register("c1", callback1) 1700 callbacks.Register("c2", callback2) 1701 require.Nil(t, ctrl1.Deactivate(ctx)) 1702 1703 start := time.Now() 1704 executed = callbacks.CycleCallback(shouldNotAbort) 1705 d = time.Since(start) 1706 1707 assert.True(t, executed) 1708 assert.Equal(t, 0, executedCounter1) 1709 assert.Equal(t, 1, executedCounter2) 1710 assert.GreaterOrEqual(t, d, 25*time.Millisecond) 1711 }) 1712 1713 t.Run("4 executable callbacks, all deactivated at different time", func(t *testing.T) { 1714 executedCounter1 := 0 1715 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1716 time.Sleep(25 * time.Millisecond) 1717 executedCounter1++ 1718 return true 1719 } 1720 executedCounter2 := 0 1721 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1722 time.Sleep(25 * time.Millisecond) 1723 executedCounter2++ 1724 return true 1725 } 1726 executedCounter3 := 0 1727 callback3 := func(shouldAbort ShouldAbortCallback) bool { 1728 time.Sleep(25 * time.Millisecond) 1729 executedCounter3++ 1730 return true 1731 } 1732 executedCounter4 := 0 1733 callback4 := func(shouldAbort ShouldAbortCallback) bool { 1734 time.Sleep(25 * time.Millisecond) 1735 executedCounter4++ 1736 return true 1737 } 1738 var executed1 bool 1739 var executed2 bool 1740 var executed3 bool 1741 var executed4 bool 1742 var d1 time.Duration 1743 var d2 time.Duration 1744 var d3 time.Duration 1745 var d4 time.Duration 1746 1747 callbacks := NewCallbackGroup("id", logger, 1) 1748 ctrl1 := callbacks.Register("c1", callback1) 1749 ctrl2 := callbacks.Register("c2", callback2) 1750 ctrl3 := callbacks.Register("c3", callback3) 1751 ctrl4 := callbacks.Register("c4", callback4) 1752 require.Nil(t, ctrl3.Deactivate(ctx)) 1753 1754 start := time.Now() 1755 executed1 = callbacks.CycleCallback(shouldNotAbort) 1756 d1 = time.Since(start) 1757 1758 require.Nil(t, ctrl1.Deactivate(ctx)) 1759 1760 start = time.Now() 1761 executed2 = callbacks.CycleCallback(shouldNotAbort) 1762 d2 = time.Since(start) 1763 1764 require.Nil(t, ctrl4.Deactivate(ctx)) 1765 1766 start = time.Now() 1767 executed3 = callbacks.CycleCallback(shouldNotAbort) 1768 d3 = time.Since(start) 1769 1770 require.Nil(t, ctrl2.Deactivate(ctx)) 1771 1772 start = time.Now() 1773 executed4 = callbacks.CycleCallback(shouldNotAbort) 1774 d4 = time.Since(start) 1775 1776 assert.True(t, executed1) 1777 assert.True(t, executed2) 1778 assert.True(t, executed3) 1779 assert.False(t, executed4) 1780 assert.Equal(t, 1, executedCounter1) 1781 assert.Equal(t, 3, executedCounter2) 1782 assert.Equal(t, 0, executedCounter3) 1783 assert.Equal(t, 2, executedCounter4) 1784 assert.GreaterOrEqual(t, d1, 75*time.Millisecond) 1785 assert.GreaterOrEqual(t, d2, 50*time.Millisecond) 1786 assert.GreaterOrEqual(t, d3, 25*time.Millisecond) 1787 assert.GreaterOrEqual(t, d4, 0*time.Millisecond) 1788 }) 1789 1790 t.Run("deactivate is waiting till the end of execution", func(t *testing.T) { 1791 executedCounter := 0 1792 callback := func(shouldAbort ShouldAbortCallback) bool { 1793 time.Sleep(50 * time.Millisecond) 1794 executedCounter++ 1795 return true 1796 } 1797 chStarted := make(chan struct{}, 1) 1798 chFinished := make(chan struct{}, 1) 1799 var executed bool 1800 var d time.Duration 1801 1802 callbacks := NewCallbackGroup("id", logger, 1) 1803 ctrl := callbacks.Register("c", callback) 1804 1805 go func() { 1806 chStarted <- struct{}{} 1807 start := time.Now() 1808 executed = callbacks.CycleCallback(shouldNotAbort) 1809 d = time.Since(start) 1810 chFinished <- struct{}{} 1811 }() 1812 <-chStarted 1813 start := time.Now() 1814 time.Sleep(25 * time.Millisecond) 1815 require.Nil(t, ctrl.Deactivate(ctx)) 1816 du := time.Since(start) 1817 <-chFinished 1818 1819 assert.True(t, executed) 1820 assert.Equal(t, 1, executedCounter) 1821 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 1822 assert.GreaterOrEqual(t, du, 40*time.Millisecond) 1823 }) 1824 1825 t.Run("deactivate fails due to context timeout", func(t *testing.T) { 1826 executedCounter := 0 1827 callback := func(shouldAbort ShouldAbortCallback) bool { 1828 time.Sleep(50 * time.Millisecond) 1829 executedCounter++ 1830 return true 1831 } 1832 chStarted := make(chan struct{}, 1) 1833 chFinished := make(chan struct{}, 1) 1834 var executed1 bool 1835 var executed2 bool 1836 var d1 time.Duration 1837 var d2 time.Duration 1838 1839 callbacks := NewCallbackGroup("id", logger, 1) 1840 ctrl := callbacks.Register("c", callback) 1841 1842 go func() { 1843 chStarted <- struct{}{} 1844 start := time.Now() 1845 executed1 = callbacks.CycleCallback(shouldNotAbort) 1846 d1 = time.Since(start) 1847 chFinished <- struct{}{} 1848 }() 1849 <-chStarted 1850 start := time.Now() 1851 time.Sleep(25 * time.Millisecond) 1852 ctxTimeout, cancel := context.WithTimeout(ctx, 5*time.Millisecond) 1853 defer cancel() 1854 require.NotNil(t, ctrl.Deactivate(ctxTimeout)) 1855 du := time.Since(start) 1856 <-chFinished 1857 1858 go func() { 1859 start := time.Now() 1860 executed2 = callbacks.CycleCallback(shouldNotAbort) 1861 d2 = time.Since(start) 1862 chFinished <- struct{}{} 1863 }() 1864 <-chFinished 1865 1866 assert.True(t, executed1) 1867 assert.True(t, executed2) 1868 assert.Equal(t, 2, executedCounter) 1869 assert.GreaterOrEqual(t, d1, 50*time.Millisecond) 1870 assert.GreaterOrEqual(t, d2, 50*time.Millisecond) 1871 assert.GreaterOrEqual(t, du, 30*time.Millisecond) 1872 }) 1873 1874 t.Run("deactivate 2nd and 3rd while executing", func(t *testing.T) { 1875 executedCounter1 := 0 1876 callback1 := func(shouldAbort ShouldAbortCallback) bool { 1877 time.Sleep(50 * time.Millisecond) 1878 executedCounter1++ 1879 return true 1880 } 1881 executedCounter2 := 0 1882 callback2 := func(shouldAbort ShouldAbortCallback) bool { 1883 time.Sleep(50 * time.Millisecond) 1884 executedCounter2++ 1885 return true 1886 } 1887 executedCounter3 := 0 1888 callback3 := func(shouldAbort ShouldAbortCallback) bool { 1889 time.Sleep(50 * time.Millisecond) 1890 executedCounter3++ 1891 return true 1892 } 1893 chStarted := make(chan struct{}, 1) 1894 chFinished := make(chan struct{}, 1) 1895 var executed bool 1896 var d time.Duration 1897 1898 callbacks := NewCallbackGroup("id", logger, 1) 1899 callbacks.Register("c1", callback1) 1900 ctrl2 := callbacks.Register("c2", callback2) 1901 ctrl3 := callbacks.Register("c3", callback3) 1902 1903 go func() { 1904 chStarted <- struct{}{} 1905 start := time.Now() 1906 executed = callbacks.CycleCallback(shouldNotAbort) 1907 d = time.Since(start) 1908 chFinished <- struct{}{} 1909 }() 1910 <-chStarted 1911 time.Sleep(25 * time.Millisecond) 1912 require.Nil(t, ctrl2.Deactivate(ctx)) 1913 require.Nil(t, ctrl3.Deactivate(ctx)) 1914 <-chFinished 1915 1916 assert.True(t, executed) 1917 assert.Equal(t, 1, executedCounter1) 1918 assert.Equal(t, 0, executedCounter2) 1919 assert.Equal(t, 0, executedCounter3) 1920 assert.GreaterOrEqual(t, d, 50*time.Millisecond) 1921 }) 1922 1923 t.Run("deactivate while running", func(t *testing.T) { 1924 counter := 0 1925 max := 25 1926 callback := func(shouldAbort ShouldAbortCallback) bool { 1927 for { 1928 if shouldAbort() { 1929 return false 1930 } 1931 1932 time.Sleep(10 * time.Millisecond) 1933 counter++ 1934 1935 // 10ms * 25 = 250ms 1936 if counter > max { 1937 return true 1938 } 1939 } 1940 } 1941 chStarted := make(chan struct{}, 1) 1942 chFinished := make(chan struct{}, 1) 1943 var executed bool 1944 var d time.Duration 1945 1946 callbacks := NewCallbackGroup("id", logger, 1) 1947 ctrl := callbacks.Register("c", callback) 1948 1949 go func() { 1950 chStarted <- struct{}{} 1951 start := time.Now() 1952 executed = callbacks.CycleCallback(shouldNotAbort) 1953 d = time.Since(start) 1954 chFinished <- struct{}{} 1955 }() 1956 <-chStarted 1957 time.Sleep(50 * time.Millisecond) 1958 require.NoError(t, ctrl.Deactivate(ctx)) 1959 <-chFinished 1960 1961 assert.False(t, executed) 1962 assert.LessOrEqual(t, counter, max) 1963 assert.LessOrEqual(t, d, 200*time.Millisecond) 1964 1965 t.Run("does not abort after activated back again", func(t *testing.T) { 1966 require.NoError(t, ctrl.Activate()) 1967 1968 counter = 0 1969 max = 10 1970 1971 go func() { 1972 start := time.Now() 1973 executed = callbacks.CycleCallback(shouldNotAbort) 1974 d = time.Since(start) 1975 chFinished <- struct{}{} 1976 }() 1977 <-chFinished 1978 1979 assert.True(t, executed) 1980 assert.Greater(t, counter, max) 1981 assert.GreaterOrEqual(t, d, 100*time.Millisecond) 1982 }) 1983 }) 1984 }