Flutter macOS Embedder
FlutterRunLoop.mm
Go to the documentation of this file.
2 #include <vector>
3 #include "fml/logging.h"
4 
5 namespace {
6 struct Task {
7  void (^block)(void);
8  CFAbsoluteTime target_time;
9 
10  Task(void (^block)(void), CFAbsoluteTime target_time) : block(block), target_time(target_time) {}
11 };
12 
13 const CFStringRef kFlutterRunLoopMode = CFSTR("FlutterRunLoopMode");
14 
15 FlutterRunLoop* mainLoop;
16 
17 } // namespace
18 
19 @implementation FlutterRunLoop {
20  CFRunLoopRef _runLoop;
21  CFRunLoopSourceRef _source;
22  CFRunLoopTimerRef _timer;
23  std::vector<Task> _tasks;
24 }
25 
26 static void Perform(void* info) {
27  FlutterRunLoop* runner = (__bridge FlutterRunLoop*)info;
28  [runner performExpiredTasks];
29 }
30 
31 static void PerformTimer(CFRunLoopTimerRef timer, void* info) {
32  FlutterRunLoop* runner = (__bridge FlutterRunLoop*)info;
33  [runner performExpiredTasks];
34 }
35 
36 - (instancetype)init {
37  if (self = [super init]) {
38  _runLoop = CFRunLoopGetCurrent();
39  CFRunLoopSourceContext sourceContext = {
40  .info = (__bridge void*)self,
41  .perform = Perform,
42  };
43  _source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sourceContext);
44  CFRunLoopAddSource(_runLoop, _source, kCFRunLoopCommonModes);
45  CFRunLoopAddSource(_runLoop, _source, kFlutterRunLoopMode);
46 
47  CFRunLoopTimerContext timerContext = {
48  .info = (__bridge void*)self,
49  };
50  _timer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VALF, HUGE_VALF, 0, 0, PerformTimer,
51  &timerContext);
52  CFRunLoopAddTimer(_runLoop, _timer, kCFRunLoopCommonModes);
53  CFRunLoopAddTimer(_runLoop, _timer, kFlutterRunLoopMode);
54  }
55  return self;
56 }
57 
58 - (void)dealloc {
59  CFRunLoopTimerInvalidate(_timer);
60  CFRunLoopRemoveTimer(_runLoop, _timer, kCFRunLoopCommonModes);
61  CFRunLoopRemoveTimer(_runLoop, _timer, kFlutterRunLoopMode);
62  CFRunLoopSourceInvalidate(_source);
63  CFRunLoopRemoveSource(_runLoop, _source, kCFRunLoopCommonModes);
64  CFRunLoopRemoveSource(_runLoop, _source, kFlutterRunLoopMode);
65 }
66 
67 - (void)rearmTimer {
68  CFAbsoluteTime nextFireTime = HUGE_VALF;
69  for (const auto& task : _tasks) {
70  nextFireTime = std::min(nextFireTime, task.target_time);
71  }
72  CFRunLoopTimerSetNextFireDate(_timer, nextFireTime);
73 }
74 
75 - (void)performExpiredTasks {
76  std::vector<Task> expiredTasks;
77  @synchronized(self) {
78  CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
79  std::vector<Task>::iterator it = _tasks.begin();
80  while (it != _tasks.end()) {
81  if (it->target_time <= now) {
82  expiredTasks.push_back(std::move(*it));
83  it = _tasks.erase(it);
84  } else {
85  ++it;
86  }
87  }
88  [self rearmTimer];
89  }
90  for (const auto& task : expiredTasks) {
91  task.block();
92  }
93 }
94 
95 - (void)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay {
96  @synchronized(self) {
97  _tasks.emplace_back(block, CFAbsoluteTimeGetCurrent() + delay);
98  if (delay > 0) {
99  [self rearmTimer];
100  } else {
101  CFRunLoopSourceSignal(_source);
102  CFRunLoopWakeUp(_runLoop);
103  }
104  }
105 }
106 
107 - (void)performBlock:(void (^)(void))block {
108  [self performBlock:block afterDelay:0];
109 }
110 
111 + (void)ensureMainLoopInitialized {
112  FML_DCHECK(NSRunLoop.currentRunLoop == NSRunLoop.mainRunLoop);
113  if (mainLoop == nil) {
114  mainLoop = [[FlutterRunLoop alloc] init];
115  }
116 }
117 
118 + (FlutterRunLoop*)mainRunLoop {
119  FML_DCHECK(mainLoop != nil);
120  return mainLoop;
121 }
122 
123 - (void)pollFlutterMessagesOnce {
124  CFRunLoopRunInMode(kFlutterRunLoopMode, 0.1, YES);
125 }
126 
127 @end
std::vector< Task > _tasks
CFRunLoopSourceRef _source
CFRunLoopTimerRef _timer