FBRunLoopSpinner.m 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "FBRunLoopSpinner.h"
  10. #import <stdatomic.h>
  11. #import "FBErrorBuilder.h"
  12. static const NSTimeInterval FBWaitInterval = 0.1;
  13. @interface FBRunLoopSpinner ()
  14. @property (nonatomic, copy) NSString *timeoutErrorMessage;
  15. @property (nonatomic, assign) NSTimeInterval timeout;
  16. @property (nonatomic, assign) NSTimeInterval interval;
  17. @end
  18. @implementation FBRunLoopSpinner
  19. + (void)spinUntilCompletion:(void (^)(void(^completion)(void)))block
  20. {
  21. __block volatile atomic_bool didFinish = false;
  22. block(^{
  23. atomic_fetch_or(&didFinish, true);
  24. });
  25. while (!atomic_fetch_and(&didFinish, false)) {
  26. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:FBWaitInterval]];
  27. }
  28. }
  29. - (instancetype)init
  30. {
  31. self = [super init];
  32. if (self) {
  33. _interval = FBWaitInterval;
  34. _timeout = 60;
  35. }
  36. return self;
  37. }
  38. - (instancetype)timeoutErrorMessage:(NSString *)timeoutErrorMessage
  39. {
  40. self.timeoutErrorMessage = timeoutErrorMessage;
  41. return self;
  42. }
  43. - (instancetype)timeout:(NSTimeInterval)timeout
  44. {
  45. self.timeout = timeout;
  46. return self;
  47. }
  48. - (instancetype)interval:(NSTimeInterval)interval
  49. {
  50. self.interval = interval;
  51. return self;
  52. }
  53. - (BOOL)spinUntilTrue:(FBRunLoopSpinnerBlock)untilTrue
  54. {
  55. return [self spinUntilTrue:untilTrue error:nil];
  56. }
  57. - (BOOL)spinUntilTrue:(FBRunLoopSpinnerBlock)untilTrue error:(NSError **)error
  58. {
  59. NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:self.timeout];
  60. while (!untilTrue()) {
  61. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:self.interval]];
  62. if (timeoutDate.timeIntervalSinceNow < 0) {
  63. return
  64. [[[FBErrorBuilder builder]
  65. withDescription:(self.timeoutErrorMessage ?: @"FBRunLoopSpinner timeout")]
  66. buildError:error];
  67. }
  68. }
  69. return YES;
  70. }
  71. - (id)spinUntilNotNil:(FBRunLoopSpinnerObjectBlock)untilNotNil error:(NSError **)error
  72. {
  73. __block id object;
  74. [self spinUntilTrue:^BOOL{
  75. object = untilNotNil();
  76. return object != nil;
  77. } error:error];
  78. return object;
  79. }
  80. @end