├── Example ├── AndroidManifest.template.xml ├── Bin │ ├── FastMM_FullDebugMode.dll │ └── FastMM_FullDebugMode64.dll ├── Entitlement.TemplateOSX32.xml ├── Entitlement.TemplateiOS.xml ├── FMain.fmx ├── FMain.pas ├── ProjectTimerQueues.groupproj ├── TimerQueues.deployproj ├── TimerQueues.dpr ├── TimerQueues.dproj ├── TimerQueues.res ├── TimerQueuesConsole.deployproj ├── TimerQueuesConsole.dpr ├── TimerQueuesConsole.dproj ├── TimerQueuesConsole.res ├── info.plist.TemplateOSX.xml └── info.plist.TemplateiOS.xml └── README.md /Example/AndroidManifest.template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | <%uses-permission%> 12 | 13 | 21 | 22 | <%application-meta-data%> 23 | <%services%> 24 | 26 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | <%activity%> 39 | <%receivers%> 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/Bin/FastMM_FullDebugMode.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grijjy/DelphiPlatformTimerQueue/6a45dcafa79dffaa6a17c671e6ede385fce51deb/Example/Bin/FastMM_FullDebugMode.dll -------------------------------------------------------------------------------- /Example/Bin/FastMM_FullDebugMode64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grijjy/DelphiPlatformTimerQueue/6a45dcafa79dffaa6a17c671e6ede385fce51deb/Example/Bin/FastMM_FullDebugMode64.dll -------------------------------------------------------------------------------- /Example/Entitlement.TemplateOSX32.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | <%appSandboxKeys%> 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Entitlement.TemplateiOS.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | <%getTaskAllowKey%> 6 | <%applicationIdentifier%> 7 | <%pushNotificationKey%> 8 | <%keychainAccessGroups%> 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/FMain.fmx: -------------------------------------------------------------------------------- 1 | object FormMain: TFormMain 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form8' 5 | ClientHeight = 628 6 | ClientWidth = 373 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | OnCreate = FormCreate 11 | OnClose = FormClose 12 | DesignerMasterStyle = 0 13 | object EditTimers: TEdit 14 | Touch.InteractiveGestures = [LongTap, DoubleTap] 15 | TabOrder = 1 16 | Text = '1' 17 | Position.X = 16.000000000000000000 18 | Position.Y = 40.000000000000000000 19 | Size.Width = 57.000000000000000000 20 | Size.Height = 32.000000000000000000 21 | Size.PlatformDefault = False 22 | end 23 | object ButtonStart: TButton 24 | Position.X = 8.000000000000000000 25 | Position.Y = 96.000000000000000000 26 | Size.Width = 80.000000000000000000 27 | Size.Height = 25.000000000000000000 28 | Size.PlatformDefault = False 29 | TabOrder = 6 30 | Text = 'Start' 31 | OnClick = ButtonStartClick 32 | end 33 | object EditInterval: TEdit 34 | Touch.InteractiveGestures = [LongTap, DoubleTap] 35 | TabOrder = 0 36 | Text = '1000' 37 | Position.X = 88.000000000000000000 38 | Position.Y = 40.000000000000000000 39 | Size.Width = 81.000000000000000000 40 | Size.Height = 32.000000000000000000 41 | Size.PlatformDefault = False 42 | end 43 | object LabelTimers: TLabel 44 | Position.X = 16.000000000000000000 45 | Position.Y = 16.000000000000000000 46 | Size.Width = 57.000000000000000000 47 | Size.Height = 17.000000000000000000 48 | Size.PlatformDefault = False 49 | Text = 'Timers' 50 | TabOrder = 8 51 | end 52 | object LabelInterval: TLabel 53 | Position.X = 88.000000000000000000 54 | Position.Y = 16.000000000000000000 55 | Size.Width = 113.000000000000000000 56 | Size.Height = 17.000000000000000000 57 | Size.PlatformDefault = False 58 | Text = 'Interval (ms)' 59 | TabOrder = 7 60 | end 61 | object ButtonStopAll: TButton 62 | Position.X = 184.000000000000000000 63 | Position.Y = 96.000000000000000000 64 | Size.Width = 80.000000000000000000 65 | Size.Height = 25.000000000000000000 66 | Size.PlatformDefault = False 67 | TabOrder = 4 68 | Text = 'Stop All' 69 | OnClick = ButtonStopAllClick 70 | end 71 | object MemoLog: TMemo 72 | Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] 73 | DataDetectorTypes = [] 74 | Position.X = 8.000000000000000000 75 | Position.Y = 352.000000000000000000 76 | Size.Width = 353.000000000000000000 77 | Size.Height = 265.000000000000000000 78 | Size.PlatformDefault = False 79 | TabOrder = 10 80 | Viewport.Width = 349.000000000000000000 81 | Viewport.Height = 261.000000000000000000 82 | end 83 | object ListViewTimers: TListView 84 | ItemAppearanceClassName = 'TListItemAppearance' 85 | ItemEditAppearanceClassName = 'TListItemShowCheckAppearance' 86 | HeaderAppearanceClassName = 'TListHeaderObjects' 87 | FooterAppearanceClassName = 'TListHeaderObjects' 88 | Position.X = 8.000000000000000000 89 | Position.Y = 128.000000000000000000 90 | Size.Width = 353.000000000000000000 91 | Size.Height = 177.000000000000000000 92 | Size.PlatformDefault = False 93 | TabOrder = 9 94 | end 95 | object ButtonStop: TButton 96 | Position.X = 96.000000000000000000 97 | Position.Y = 96.000000000000000000 98 | Size.Width = 80.000000000000000000 99 | Size.Height = 25.000000000000000000 100 | Size.PlatformDefault = False 101 | TabOrder = 2 102 | Text = 'Stop' 103 | OnClick = ButtonStopClick 104 | end 105 | object ButtonClear: TButton 106 | Position.X = 8.000000000000000000 107 | Position.Y = 320.000000000000000000 108 | Size.Width = 80.000000000000000000 109 | Size.Height = 25.000000000000000000 110 | Size.PlatformDefault = False 111 | TabOrder = 5 112 | Text = 'Clear' 113 | OnClick = ButtonClearClick 114 | end 115 | object ButtonSetInterval: TButton 116 | Position.X = 272.000000000000000000 117 | Position.Y = 96.000000000000000000 118 | Size.Width = 80.000000000000000000 119 | Size.Height = 25.000000000000000000 120 | Size.PlatformDefault = False 121 | TabOrder = 3 122 | Text = 'Set Interval' 123 | OnClick = ButtonSetIntervalClick 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /Example/FMain.pas: -------------------------------------------------------------------------------- 1 | unit FMain; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, 7 | System.Types, 8 | System.UITypes, 9 | System.Classes, 10 | System.Variants, 11 | FMX.Types, 12 | FMX.Controls, 13 | FMX.Forms, 14 | FMX.Graphics, 15 | FMX.Dialogs, 16 | FMX.ScrollBox, 17 | FMX.Memo, 18 | FMX.StdCtrls, 19 | FMX.Controls.Presentation, 20 | FMX.Edit, 21 | FMX.ListView.Types, 22 | FMX.ListView.Appearances, 23 | FMX.ListView.Adapters.Base, 24 | FMX.ListView, 25 | System.Generics.Collections, 26 | Grijjy.TimerQueue; 27 | 28 | type 29 | TFormMain = class(TForm) 30 | EditTimers: TEdit; 31 | ButtonStart: TButton; 32 | EditInterval: TEdit; 33 | LabelTimers: TLabel; 34 | LabelInterval: TLabel; 35 | ButtonStopAll: TButton; 36 | MemoLog: TMemo; 37 | ListViewTimers: TListView; 38 | ButtonStop: TButton; 39 | ButtonClear: TButton; 40 | ButtonSetInterval: TButton; 41 | procedure FormCreate(Sender: TObject); 42 | procedure ButtonStartClick(Sender: TObject); 43 | procedure FormClose(Sender: TObject; var Action: TCloseAction); 44 | procedure ButtonStopAllClick(Sender: TObject); 45 | procedure ButtonStopClick(Sender: TObject); 46 | procedure ButtonClearClick(Sender: TObject); 47 | procedure ButtonSetIntervalClick(Sender: TObject); 48 | private 49 | { Private declarations } 50 | FTimerQueue: TgoTimerQueue; 51 | 52 | procedure OnTimer(const ASender: TObject); 53 | public 54 | { Public declarations } 55 | end; 56 | 57 | var 58 | FormMain: TFormMain; 59 | 60 | implementation 61 | 62 | {$R *.fmx} 63 | 64 | procedure TFormMain.FormCreate(Sender: TObject); 65 | begin 66 | FTimerQueue := TgoTimerQueue.Create; 67 | end; 68 | 69 | procedure TFormMain.FormClose(Sender: TObject; var Action: TCloseAction); 70 | begin 71 | FTimerQueue.Free; 72 | end; 73 | 74 | procedure TFormMain.ButtonStartClick(Sender: TObject); 75 | var 76 | Timers, Interval: Integer; 77 | Handle: THandle; 78 | I: Integer; 79 | Item: TListViewItem; 80 | begin 81 | Timers := StrToIntDef(EditTimers.Text, 1); 82 | Interval := StrToIntDef(EditInterval.Text, 1000); 83 | 84 | for I := 0 to Timers - 1 do 85 | begin 86 | Handle := FTimerQueue.Add(Interval, OnTimer); 87 | Item := ListViewTimers.Items.Add; 88 | Item.Tag := Handle; 89 | Item.Text := Format('Timer (%s, Interval=%d)', 90 | [UInt32(Handle).ToHexString, Interval]); 91 | end; 92 | end; 93 | 94 | procedure TFormMain.ButtonStopAllClick(Sender: TObject); 95 | var 96 | Handle: THandle; 97 | Item: TListViewItem; 98 | begin 99 | for Item in ListViewTimers.Items do 100 | begin 101 | Handle := Item.Tag; 102 | FTimerQueue.Release(Handle); 103 | end; 104 | ListViewTimers.Items.Clear; 105 | end; 106 | 107 | procedure TFormMain.ButtonStopClick(Sender: TObject); 108 | var 109 | Handle: THandle; 110 | begin 111 | if ListViewTimers.ItemIndex > -1 then 112 | begin 113 | Handle := ListViewTimers.Items[ListViewTimers.ItemIndex].Tag; 114 | FTimerQueue.Release(Handle); 115 | ListViewTimers.Items.Delete(ListViewTimers.ItemIndex); 116 | end; 117 | end; 118 | 119 | procedure TFormMain.ButtonSetIntervalClick(Sender: TObject); 120 | var 121 | Interval: Integer; 122 | Handle: THandle; 123 | begin 124 | Interval := StrToIntDef(EditInterval.Text, 1000); 125 | 126 | if ListViewTimers.ItemIndex > -1 then 127 | begin 128 | Handle := ListViewTimers.Items[ListViewTimers.ItemIndex].Tag; 129 | FTimerQueue.SetInterval(Handle, Interval); 130 | end; 131 | end; 132 | 133 | procedure TFormMain.ButtonClearClick(Sender: TObject); 134 | begin 135 | MemoLog.Lines.Clear; 136 | end; 137 | 138 | procedure TFormMain.OnTimer(const ASender: TObject); 139 | var 140 | S: String; 141 | Timer: TgoTimer; 142 | begin 143 | Timer := ASender as TgoTimer; 144 | S := (Format('OnTimer (%s, Thread=%s, Interval=%d)', 145 | [UInt32(Timer.Handle).ToHexString, TThread.CurrentThread.ThreadID.ToHexString, Timer.Interval])); 146 | 147 | TThread.Synchronize(nil, 148 | procedure 149 | begin 150 | MemoLog.Lines.Add(S); 151 | MemoLog.GoToTextEnd; 152 | end); 153 | end; 154 | 155 | end. 156 | -------------------------------------------------------------------------------- /Example/ProjectTimerQueues.groupproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {A1C7F860-A3A6-49E1-92EA-BF4B298703E7} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Default.Personality.12 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Example/TimerQueues.deployproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 12 5 | 6 | 7 | 04241158012360049ddd 8 | 9 | 10 | iPhone5 11 | 12 | 13 | 14 | TimerQueues\ 15 | TimerQueues.exe 16 | ProjectOutput 17 | 0 18 | 19 | 20 | True 21 | True 22 | 23 | 24 | 25 | 26 | TimerQueues.app\ 27 | Info.plist 28 | ProjectiOSInfoPList 29 | 1 30 | 31 | 32 | True 33 | 34 | 35 | TimerQueues.app\ 36 | Default-568h@2x.png 37 | iPhone_Launch640x1136 38 | 1 39 | 40 | 41 | True 42 | 43 | 44 | TimerQueues.app\ 45 | FM_ApplicationIcon_180x180.png 46 | iPhone_AppIcon180 47 | 0 48 | 49 | 50 | True 51 | 52 | 53 | TimerQueues.app\ 54 | FM_ApplicationIcon_57x57.png 55 | iPhone_AppIcon57 56 | 0 57 | 58 | 59 | True 60 | 61 | 62 | TimerQueues.app\ 63 | FM_ApplicationIcon_144x144.png 64 | iPad_AppIcon144 65 | 0 66 | 67 | 68 | True 69 | 70 | 71 | TimerQueues.app\ 72 | FM_ApplicationIcon_60x60.png 73 | iPhone_AppIcon60 74 | 0 75 | 76 | 77 | True 78 | 79 | 80 | TimerQueues.app\ 81 | Default-667h@2x.png 82 | iPhone_Launch750 83 | 0 84 | 85 | 86 | True 87 | 88 | 89 | TimerQueues.app\ 90 | FM_SettingIcon_29x29.png 91 | iPad_Setting29 92 | 0 93 | 94 | 95 | True 96 | 97 | 98 | TimerQueues.app\ 99 | Default@2x.png 100 | iPhone_Launch640 101 | 1 102 | 103 | 104 | True 105 | 106 | 107 | TimerQueues.app\ 108 | FM_SettingIcon_58x58.png 109 | iPad_Setting58 110 | 0 111 | 112 | 113 | True 114 | 115 | 116 | TimerQueues.app\ 117 | Default.png 118 | iPhone_Launch320 119 | 1 120 | 121 | 122 | True 123 | 124 | 125 | TimerQueues.app\ 126 | FM_ApplicationIcon_72x72.png 127 | iPad_AppIcon72 128 | 0 129 | 130 | 131 | True 132 | 133 | 134 | TimerQueues.app\ 135 | Default-Portrait@2x.png 136 | iPad_Launch1536 137 | 1 138 | 139 | 140 | True 141 | 142 | 143 | TimerQueues.app\ 144 | Default-Landscape@2x.png 145 | iPad_Launch2048 146 | 1 147 | 148 | 149 | True 150 | 151 | 152 | TimerQueues.app\ 153 | FM_ApplicationIcon_120x120.png 154 | iPhone_AppIcon120 155 | 0 156 | 157 | 158 | True 159 | 160 | 161 | TimerQueues.app\ 162 | Default-Portrait@2x~ipad.png 163 | iPad_Launch1536x2048 164 | 0 165 | 166 | 167 | True 168 | 169 | 170 | TimerQueues.app\ 171 | FM_SpotlightSearchIcon_100x100.png 172 | iPad_SpotLight100 173 | 0 174 | 175 | 176 | True 177 | 178 | 179 | TimerQueues.app\ 180 | FM_SpotlightSearchIcon_58x58.png 181 | iPhone_Spotlight58 182 | 0 183 | 184 | 185 | True 186 | 187 | 188 | TimerQueues.app\ 189 | TimerQueues 190 | ProjectOutput 191 | 1 192 | 193 | 194 | True 195 | True 196 | 197 | 198 | TimerQueues.app\ 199 | Default~ipad.png 200 | iPad_Launch768 201 | 1 202 | 203 | 204 | True 205 | 206 | 207 | TimerQueues.app\ 208 | Default-Landscape-812h@3x.png 209 | iPhone_Launch2436 210 | 0 211 | 212 | 213 | True 214 | 215 | 216 | TimerQueues.app\..\ 217 | TimerQueues.entitlements 218 | ProjectiOSEntitlements 219 | 1 220 | 221 | 222 | True 223 | 224 | 225 | TimerQueues.app\ 226 | Default-Portrait~ipad.png 227 | iPad_Launch768x1024 228 | 0 229 | 230 | 231 | True 232 | 233 | 234 | TimerQueues.app\ 235 | FM_ApplicationIcon_114x114.png 236 | iPhone_AppIcon114 237 | 0 238 | 239 | 240 | True 241 | 242 | 243 | TimerQueues.app\ 244 | FM_SpotlightSearchIcon_50x50.png 245 | iPad_SpotLight50 246 | 0 247 | 248 | 249 | True 250 | 251 | 252 | TimerQueues.app\ 253 | FM_ApplicationIcon_76x76.png 254 | iPad_AppIcon76 255 | 0 256 | 257 | 258 | True 259 | 260 | 261 | TimerQueues.app\ 262 | FM_SpotlightSearchIcon_80x80.png 263 | iPhone_Spotlight80 264 | 0 265 | 266 | 267 | True 268 | 269 | 270 | TimerQueues.app\ 271 | FM_SpotlightSearchIcon_80x80.png 272 | iPad_SpotLight80 273 | 0 274 | 275 | 276 | True 277 | 278 | 279 | TimerQueues.app\ 280 | Default-Landscape-736h@3x.png 281 | iPhone_Launch2208 282 | 0 283 | 284 | 285 | True 286 | 287 | 288 | TimerQueues.app\..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF\ 289 | TimerQueues 290 | ProjectiOSDeviceDebug 291 | 1 292 | 293 | 294 | True 295 | 296 | 297 | TimerQueues.app\ 298 | FM_SpotlightSearchIcon_40x40.png 299 | iPhone_Spotlight40 300 | 0 301 | 302 | 303 | True 304 | 305 | 306 | TimerQueues.app\ 307 | Default-736h@3x.png 308 | iPhone_Launch1242 309 | 0 310 | 311 | 312 | True 313 | 314 | 315 | TimerQueues.app\ 316 | Default-812h@3x.png 317 | iPhone_Launch1125 318 | 0 319 | 320 | 321 | True 322 | 323 | 324 | TimerQueues.app\ 325 | Default-Landscape.png 326 | iPad_Launch1024 327 | 1 328 | 329 | 330 | True 331 | 332 | 333 | TimerQueues.app\ 334 | FM_ApplicationIcon_152x152.png 335 | iPad_AppIcon152 336 | 0 337 | 338 | 339 | True 340 | 341 | 342 | TimerQueues.app\ 343 | FM_SpotlightSearchIcon_40x40.png 344 | iPad_SpotLight40 345 | 0 346 | 347 | 348 | True 349 | 350 | 351 | TimerQueues.app\ 352 | FM_ApplicationIcon_87x87.png 353 | iPhone_AppIcon87 354 | 0 355 | 356 | 357 | True 358 | 359 | 360 | TimerQueues.app\ 361 | FM_SpotlightSearchIcon_29x29.png 362 | iPhone_Spotlight29 363 | 0 364 | 365 | 366 | True 367 | 368 | 369 | TimerQueues.app\ 370 | ResourceRules.plist 371 | ProjectiOSDeviceResourceRules 372 | 1 373 | 374 | 375 | True 376 | 377 | 378 | TimerQueues.app\ 379 | Default-Landscape@2x~ipad.png 380 | iPad_Launch2048x1536 381 | 0 382 | 383 | 384 | True 385 | 386 | 387 | TimerQueues.app\ 388 | Default-Landscape~ipad.png 389 | iPad_Launch1024x768 390 | 0 391 | 392 | 393 | True 394 | 395 | 396 | 397 | 398 | 399 | TimerQueues\ 400 | TimerQueues.exe 401 | ProjectOutput 402 | 0 403 | 404 | 405 | True 406 | True 407 | 408 | 409 | 410 | 411 | 412 | TimerQueues.app\Contents\Resources\StartUp\ 413 | libzmq-osx32.dylib 414 | File 415 | 0 416 | 417 | 418 | True 419 | 420 | 421 | TimerQueues.app\Contents\MacOS\ 422 | TimerQueues 423 | ProjectOutput 424 | 1 425 | 426 | 427 | True 428 | True 429 | 430 | 431 | TimerQueues.app\Contents\ 432 | Info.plist 433 | ProjectOSXInfoPList 434 | 1 435 | 436 | 437 | True 438 | 439 | 440 | TimerQueues.app\Contents\Resources\ 441 | TimerQueues.icns 442 | ProjectOSXResource 443 | 1 444 | 445 | 446 | True 447 | 448 | 449 | TimerQueues.app\Contents\MacOS\ 450 | libcgsqlite3.dylib 451 | DependencyModule 452 | 1 453 | 454 | 455 | True 456 | 457 | 458 | TimerQueues.app\..\ 459 | TimerQueues.entitlements 460 | ProjectOSXEntitlements 461 | 1 462 | 463 | 464 | True 465 | 466 | 467 | TimerQueues.app\Contents\MacOS\ 468 | libcgunwind.1.0.dylib 469 | DependencyModule 470 | 1 471 | 472 | 473 | True 474 | 475 | 476 | TimerQueues.app\Contents\MacOS\ 477 | libzmq-osx32.dylib 478 | File 479 | 0 480 | 481 | 482 | True 483 | 484 | 485 | 486 | 487 | TimerQueues\res\drawable-ldpi\ 488 | ic_launcher.png 489 | Android_LauncherIcon36 490 | 1 491 | 492 | 493 | True 494 | 495 | 496 | TimerQueues\res\drawable-hdpi\ 497 | ic_launcher.png 498 | Android_LauncherIcon72 499 | 1 500 | 501 | 502 | True 503 | 504 | 505 | TimerQueues\res\drawable-small\ 506 | splash_image.png 507 | Android_SplashImage426 508 | 1 509 | 510 | 511 | True 512 | 513 | 514 | TimerQueues\library\lib\mips\ 515 | libTimerQueues.so 516 | AndroidLibnativeMipsFile 517 | 1 518 | 519 | 520 | True 521 | 522 | 523 | TimerQueues\res\drawable\ 524 | splash_image_def.xml 525 | AndroidSplashImageDef 526 | 1 527 | 528 | 529 | True 530 | 531 | 532 | TimerQueues\ 533 | AndroidManifest.xml 534 | ProjectAndroidManifest 535 | 1 536 | 537 | 538 | True 539 | 540 | 541 | TimerQueues\library\lib\armeabi\ 542 | libTimerQueues.so 543 | AndroidLibnativeArmeabiFile 544 | 1 545 | 546 | 547 | True 548 | 549 | 550 | TimerQueues\res\drawable-xxhdpi\ 551 | ic_launcher.png 552 | Android_LauncherIcon144 553 | 1 554 | 555 | 556 | True 557 | 558 | 559 | TimerQueues\res\drawable-xhdpi\ 560 | ic_launcher.png 561 | Android_LauncherIcon96 562 | 1 563 | 564 | 565 | True 566 | 567 | 568 | TimerQueues\res\drawable-large\ 569 | splash_image.png 570 | Android_SplashImage640 571 | 1 572 | 573 | 574 | True 575 | 576 | 577 | TimerQueues\res\drawable-xlarge\ 578 | splash_image.png 579 | Android_SplashImage960 580 | 1 581 | 582 | 583 | True 584 | 585 | 586 | TimerQueues\res\drawable-mdpi\ 587 | ic_launcher.png 588 | Android_LauncherIcon48 589 | 1 590 | 591 | 592 | True 593 | 594 | 595 | TimerQueues\library\lib\armeabi-v7a\ 596 | gdbserver 597 | AndroidGDBServer 598 | 1 599 | 600 | 601 | True 602 | 603 | 604 | TimerQueues\classes\ 605 | classes.dex 606 | AndroidClassesDexFile 607 | 1 608 | 609 | 610 | True 611 | 612 | 613 | TimerQueues\res\values\ 614 | styles.xml 615 | AndroidSplashStyles 616 | 1 617 | 618 | 619 | True 620 | 621 | 622 | TimerQueues\library\lib\armeabi-v7a\ 623 | libTimerQueues.so 624 | ProjectOutput 625 | 1 626 | 627 | 628 | True 629 | True 630 | 631 | 632 | TimerQueues\res\drawable-normal\ 633 | splash_image.png 634 | Android_SplashImage470 635 | 1 636 | 637 | 638 | True 639 | 640 | 641 | 642 | 643 | TimerQueues.app\ 644 | libcgunwind.1.0.dylib 645 | DependencyModule 646 | 1 647 | 648 | 649 | True 650 | 651 | 652 | TimerQueues.app\ 653 | libPCRE.dylib 654 | DependencyModule 655 | 1 656 | 657 | 658 | True 659 | 660 | 661 | 662 | -------------------------------------------------------------------------------- /Example/TimerQueues.dpr: -------------------------------------------------------------------------------- 1 | program TimerQueues; 2 | 3 | uses 4 | System.StartUpCopy, 5 | FMX.Forms, 6 | FMain in 'FMain.pas' {FormMain}; 7 | 8 | {$R *.res} 9 | 10 | begin 11 | ReportMemoryLeaksOnShutDown := True; 12 | 13 | Application.Initialize; 14 | Application.CreateForm(TFormMain, FormMain); 15 | Application.Run; 16 | end. 17 | -------------------------------------------------------------------------------- /Example/TimerQueues.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {5410AA92-BDC3-48FA-8463-B0E857819928} 4 | 18.4 5 | FMX 6 | TimerQueues.dpr 7 | True 8 | Debug 9 | Win64 10 | 1119 11 | Application 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Base 34 | true 35 | 36 | 37 | true 38 | Base 39 | true 40 | 41 | 42 | true 43 | Base 44 | true 45 | 46 | 47 | true 48 | Base 49 | true 50 | 51 | 52 | true 53 | Base 54 | true 55 | 56 | 57 | true 58 | Cfg_1 59 | true 60 | true 61 | 62 | 63 | true 64 | Cfg_1 65 | true 66 | true 67 | 68 | 69 | true 70 | Cfg_1 71 | true 72 | true 73 | 74 | 75 | true 76 | Base 77 | true 78 | 79 | 80 | true 81 | Cfg_2 82 | true 83 | true 84 | 85 | 86 | true 87 | Cfg_2 88 | true 89 | true 90 | 91 | 92 | .\$(Platform)\$(Config) 93 | .\$(Platform)\$(Config) 94 | false 95 | false 96 | false 97 | false 98 | false 99 | RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) 100 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) 101 | true 102 | true 103 | true 104 | true 105 | true 106 | true 107 | true 108 | true 109 | true 110 | true 111 | $(BDS)\bin\delphi_PROJECTICON.ico 112 | $(BDS)\bin\delphi_PROJECTICNS.icns 113 | TimerQueues 114 | ..\..\GrijjyFoundation;$(DCC_UnitSearchPath) 115 | 116 | 117 | DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 118 | package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= 119 | Debug 120 | true 121 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png 122 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png 123 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png 124 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png 125 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png 126 | $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png 127 | $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png 128 | $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png 129 | $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png 130 | android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar 131 | 132 | 133 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 134 | CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera 135 | iPhoneAndiPad 136 | true 137 | Debug 138 | $(MSBuildProjectName) 139 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png 140 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png 141 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png 142 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_114x114.png 143 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png 144 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png 145 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_320x480.png 146 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x960.png 147 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x1136.png 148 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png 149 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png 150 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png 151 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png 152 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png 153 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png 154 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png 155 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png 156 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png 157 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png 158 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png 159 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png 160 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png 161 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1004.png 162 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png 163 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x748.png 164 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png 165 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png 166 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png 167 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1496.png 168 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png 169 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png 170 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_50x50.png 171 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png 172 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_100x100.png 173 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_29x29.png 174 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png 175 | 176 | 177 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 178 | CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera 179 | iPhoneAndiPad 180 | true 181 | Debug 182 | $(MSBuildProjectName) 183 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png 184 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png 185 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png 186 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_114x114.png 187 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png 188 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png 189 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_320x480.png 190 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x960.png 191 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x1136.png 192 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png 193 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png 194 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png 195 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png 196 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png 197 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png 198 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png 199 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png 200 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png 201 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png 202 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png 203 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png 204 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png 205 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1004.png 206 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png 207 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x748.png 208 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png 209 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png 210 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png 211 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1496.png 212 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png 213 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png 214 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_50x50.png 215 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png 216 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_100x100.png 217 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_29x29.png 218 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png 219 | 220 | 221 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 222 | CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera 223 | iPhoneAndiPad 224 | true 225 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png 226 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png 227 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png 228 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_114x114.png 229 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png 230 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png 231 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_320x480.png 232 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x960.png 233 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x1136.png 234 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png 235 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png 236 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png 237 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png 238 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png 239 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png 240 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png 241 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png 242 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png 243 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png 244 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png 245 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png 246 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png 247 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1004.png 248 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png 249 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x748.png 250 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png 251 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png 252 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png 253 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1496.png 254 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png 255 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png 256 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_50x50.png 257 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png 258 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_100x100.png 259 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_29x29.png 260 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png 261 | 262 | 263 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) 264 | CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts 265 | Debug 266 | true 267 | 268 | 269 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;svnui;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;Grijjy.Package.RTL;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;TMSListControlsDXE11;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;emshosting;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;Grijjy.Package.FMX;DataSnapServerMidas;$(DCC_UsePackage) 270 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 271 | Debug 272 | true 273 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 274 | 1033 275 | $(BDS)\bin\default_app.manifest 276 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 277 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 278 | .\Bin 279 | 280 | 281 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;emshosting;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) 282 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) 283 | Debug 284 | true 285 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 286 | 1033 287 | $(BDS)\bin\default_app.manifest 288 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 289 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 290 | .\Bin 291 | 292 | 293 | DEBUG;$(DCC_Define) 294 | true 295 | false 296 | true 297 | true 298 | true 299 | 300 | 301 | true 302 | iPhoneAndiPad 303 | $(MSBuildProjectName) 304 | 305 | 306 | false 307 | true 308 | true 309 | true 310 | 1033 311 | 312 | 313 | true 314 | true 315 | true 316 | 1033 317 | 318 | 319 | false 320 | RELEASE;$(DCC_Define) 321 | 0 322 | 0 323 | 324 | 325 | true 326 | true 327 | 328 | 329 | true 330 | true 331 | 332 | 333 | 334 | MainSource 335 | 336 | 337 |
FormMain
338 | fmx 339 |
340 | 341 | Cfg_2 342 | Base 343 | 344 | 345 | Base 346 | 347 | 348 | Cfg_1 349 | Base 350 | 351 |
352 | 353 | Delphi.Personality.12 354 | Application 355 | 356 | 357 | 358 | TimerQueues.dpr 359 | 360 | 361 | Microsoft Office 2000 Sample Automation Server Wrapper Components 362 | Microsoft Office XP Sample Automation Server Wrapper Components 363 | 364 | 365 | 366 | 367 | 368 | ic_launcher.png 369 | true 370 | 371 | 372 | 373 | 374 | Info.plist 375 | true 376 | 377 | 378 | 379 | 380 | ic_launcher.png 381 | true 382 | 383 | 384 | 385 | 386 | Default-568h@2x.png 387 | true 388 | 389 | 390 | 391 | 392 | true 393 | 394 | 395 | 396 | 397 | true 398 | 399 | 400 | 401 | 402 | true 403 | 404 | 405 | 406 | 407 | splash_image.png 408 | true 409 | 410 | 411 | 412 | 413 | libzmq-osx32.dylib 414 | true 415 | 416 | 417 | 418 | 419 | true 420 | 421 | 422 | 423 | 424 | Default-667h@2x.png 425 | true 426 | 427 | 428 | 429 | 430 | libTimerQueues.so 431 | true 432 | 433 | 434 | 435 | 436 | true 437 | 438 | 439 | 440 | 441 | true 442 | 443 | 444 | 445 | 446 | TimerQueues 447 | true 448 | 449 | 450 | 451 | 452 | Default@2x.png 453 | true 454 | 455 | 456 | 457 | 458 | TimerQueues.exe 459 | true 460 | 461 | 462 | 463 | 464 | Info.plist 465 | true 466 | 467 | 468 | 469 | 470 | true 471 | 472 | 473 | 474 | 475 | true 476 | 477 | 478 | 479 | 480 | libTimerQueues.so 481 | true 482 | 483 | 484 | 485 | 486 | ic_launcher.png 487 | true 488 | 489 | 490 | 491 | 492 | true 493 | 494 | 495 | 496 | 497 | TimerQueues.icns 498 | true 499 | 500 | 501 | 502 | 503 | true 504 | 505 | 506 | 507 | 508 | true 509 | 510 | 511 | 512 | 513 | Default.png 514 | true 515 | 516 | 517 | 518 | 519 | true 520 | 521 | 522 | 523 | 524 | Default-Portrait@2x.png 525 | true 526 | 527 | 528 | 529 | 530 | true 531 | 532 | 533 | 534 | 535 | ic_launcher.png 536 | true 537 | 538 | 539 | 540 | 541 | Default-Landscape@2x.png 542 | true 543 | 544 | 545 | 546 | 547 | TimerQueues.exe 548 | true 549 | 550 | 551 | 552 | 553 | true 554 | 555 | 556 | 557 | 558 | Default-Portrait@2x~ipad.png 559 | true 560 | 561 | 562 | 563 | 564 | TimerQueues.rsm 565 | true 566 | 567 | 568 | 569 | 570 | true 571 | 572 | 573 | 574 | 575 | true 576 | 577 | 578 | 579 | 580 | TimerQueues 581 | true 582 | 583 | 584 | 585 | 586 | Default~ipad.png 587 | true 588 | 589 | 590 | 591 | 592 | Default-Landscape-812h@3x.png 593 | true 594 | 595 | 596 | 597 | 598 | true 599 | 600 | 601 | 602 | 603 | Default-Portrait~ipad.png 604 | true 605 | 606 | 607 | 608 | 609 | splash_image.png 610 | true 611 | 612 | 613 | 614 | 615 | splash_image.png 616 | true 617 | 618 | 619 | 620 | 621 | true 622 | 623 | 624 | 625 | 626 | true 627 | 628 | 629 | 630 | 631 | true 632 | 633 | 634 | 635 | 636 | true 637 | 638 | 639 | 640 | 641 | true 642 | 643 | 644 | 645 | 646 | ic_launcher.png 647 | true 648 | 649 | 650 | 651 | 652 | true 653 | 654 | 655 | 656 | 657 | Default-Landscape-736h@3x.png 658 | true 659 | 660 | 661 | 662 | 663 | true 664 | 665 | 666 | 667 | 668 | classes.dex 669 | true 670 | 671 | 672 | 673 | 674 | TimerQueues 675 | true 676 | 677 | 678 | 679 | 680 | true 681 | 682 | 683 | 684 | 685 | true 686 | 687 | 688 | 689 | 690 | Default-736h@3x.png 691 | true 692 | 693 | 694 | 695 | 696 | libTimerQueues.so 697 | true 698 | 699 | 700 | 701 | 702 | splash_image.png 703 | true 704 | 705 | 706 | 707 | 708 | Default-812h@3x.png 709 | true 710 | 711 | 712 | 713 | 714 | Default-Landscape.png 715 | true 716 | 717 | 718 | 719 | 720 | true 721 | 722 | 723 | 724 | 725 | true 726 | 727 | 728 | 729 | 730 | true 731 | 732 | 733 | 734 | 735 | true 736 | 737 | 738 | 739 | 740 | ResourceRules.plist 741 | true 742 | 743 | 744 | 745 | 746 | Default-Landscape@2x~ipad.png 747 | true 748 | 749 | 750 | 751 | 752 | Default-Landscape~ipad.png 753 | true 754 | 755 | 756 | 757 | 758 | Contents\MacOS\ 759 | libzmq-osx32.dylib 760 | true 761 | 762 | 763 | 764 | 765 | 1 766 | 767 | 768 | Contents\MacOS 769 | 1 770 | 771 | 772 | Contents\MacOS 773 | 0 774 | 775 | 776 | 777 | 778 | classes 779 | 1 780 | 781 | 782 | 783 | 784 | library\lib\armeabi-v7a 785 | 1 786 | 787 | 788 | 789 | 790 | library\lib\armeabi 791 | 1 792 | 793 | 794 | 795 | 796 | library\lib\mips 797 | 1 798 | 799 | 800 | 801 | 802 | library\lib\armeabi-v7a 803 | 1 804 | 805 | 806 | 807 | 808 | res\drawable 809 | 1 810 | 811 | 812 | 813 | 814 | res\values 815 | 1 816 | 817 | 818 | 819 | 820 | res\drawable 821 | 1 822 | 823 | 824 | 825 | 826 | res\drawable-xxhdpi 827 | 1 828 | 829 | 830 | 831 | 832 | res\drawable-ldpi 833 | 1 834 | 835 | 836 | 837 | 838 | res\drawable-mdpi 839 | 1 840 | 841 | 842 | 843 | 844 | res\drawable-hdpi 845 | 1 846 | 847 | 848 | 849 | 850 | res\drawable-xhdpi 851 | 1 852 | 853 | 854 | 855 | 856 | res\drawable-small 857 | 1 858 | 859 | 860 | 861 | 862 | res\drawable-normal 863 | 1 864 | 865 | 866 | 867 | 868 | res\drawable-large 869 | 1 870 | 871 | 872 | 873 | 874 | res\drawable-xlarge 875 | 1 876 | 877 | 878 | 879 | 880 | 1 881 | 882 | 883 | Contents\MacOS 884 | 1 885 | 886 | 887 | 0 888 | 889 | 890 | 891 | 892 | Contents\MacOS 893 | 1 894 | .framework 895 | 896 | 897 | 0 898 | 899 | 900 | 901 | 902 | 1 903 | .dylib 904 | 905 | 906 | 1 907 | .dylib 908 | 909 | 910 | 1 911 | .dylib 912 | 913 | 914 | Contents\MacOS 915 | 1 916 | .dylib 917 | 918 | 919 | 0 920 | .dll;.bpl 921 | 922 | 923 | 924 | 925 | 1 926 | .dylib 927 | 928 | 929 | 1 930 | .dylib 931 | 932 | 933 | 1 934 | .dylib 935 | 936 | 937 | Contents\MacOS 938 | 1 939 | .dylib 940 | 941 | 942 | 0 943 | .bpl 944 | 945 | 946 | 947 | 948 | 0 949 | 950 | 951 | 0 952 | 953 | 954 | 0 955 | 956 | 957 | 0 958 | 959 | 960 | Contents\Resources\StartUp\ 961 | 0 962 | 963 | 964 | 0 965 | 966 | 967 | 968 | 969 | 1 970 | 971 | 972 | 1 973 | 974 | 975 | 1 976 | 977 | 978 | 979 | 980 | 1 981 | 982 | 983 | 1 984 | 985 | 986 | 1 987 | 988 | 989 | 990 | 991 | 1 992 | 993 | 994 | 1 995 | 996 | 997 | 1 998 | 999 | 1000 | 1001 | 1002 | 1 1003 | 1004 | 1005 | 1 1006 | 1007 | 1008 | 1 1009 | 1010 | 1011 | 1012 | 1013 | 1 1014 | 1015 | 1016 | 1 1017 | 1018 | 1019 | 1 1020 | 1021 | 1022 | 1023 | 1024 | 1 1025 | 1026 | 1027 | 1 1028 | 1029 | 1030 | 1 1031 | 1032 | 1033 | 1034 | 1035 | 1 1036 | 1037 | 1038 | 1 1039 | 1040 | 1041 | 1 1042 | 1043 | 1044 | 1045 | 1046 | 1 1047 | 1048 | 1049 | 1050 | 1051 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1052 | 1 1053 | 1054 | 1055 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1056 | 1 1057 | 1058 | 1059 | 1060 | 1061 | 1 1062 | 1063 | 1064 | 1 1065 | 1066 | 1067 | 1068 | 1069 | ..\ 1070 | 1 1071 | 1072 | 1073 | ..\ 1074 | 1 1075 | 1076 | 1077 | 1078 | 1079 | 1 1080 | 1081 | 1082 | 1 1083 | 1084 | 1085 | 1 1086 | 1087 | 1088 | 1089 | 1090 | 1 1091 | 1092 | 1093 | 1 1094 | 1095 | 1096 | 1 1097 | 1098 | 1099 | 1100 | 1101 | ..\ 1102 | 1 1103 | 1104 | 1105 | 1106 | 1107 | Contents 1108 | 1 1109 | 1110 | 1111 | 1112 | 1113 | Contents\Resources 1114 | 1 1115 | 1116 | 1117 | 1118 | 1119 | library\lib\armeabi-v7a 1120 | 1 1121 | 1122 | 1123 | 1 1124 | 1125 | 1126 | 1 1127 | 1128 | 1129 | 1 1130 | 1131 | 1132 | 1 1133 | 1134 | 1135 | Contents\MacOS 1136 | 1 1137 | 1138 | 1139 | 0 1140 | 1141 | 1142 | 1143 | 1144 | 1 1145 | 1146 | 1147 | 1 1148 | 1149 | 1150 | 1151 | 1152 | Assets 1153 | 1 1154 | 1155 | 1156 | Assets 1157 | 1 1158 | 1159 | 1160 | 1161 | 1162 | Assets 1163 | 1 1164 | 1165 | 1166 | Assets 1167 | 1 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | True 1181 | True 1182 | True 1183 | True 1184 | True 1185 | True 1186 | True 1187 | 1188 | 1189 | 12 1190 | 1191 | 1192 | 1193 | 1194 |
1195 | -------------------------------------------------------------------------------- /Example/TimerQueues.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grijjy/DelphiPlatformTimerQueue/6a45dcafa79dffaa6a17c671e6ede385fce51deb/Example/TimerQueues.res -------------------------------------------------------------------------------- /Example/TimerQueuesConsole.deployproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 12 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | TimerQueuesConsole\ 18 | TimerQueuesConsole.exe 19 | ProjectOutput 20 | 0 21 | 22 | 23 | True 24 | True 25 | 26 | 27 | 28 | 29 | TimerQueuesConsole\ 30 | libzmq-linux64.so 31 | File 32 | 0 33 | 34 | 35 | True 36 | 37 | 38 | TimerQueuesConsole\ 39 | libzmq-linux64.so 40 | File 41 | 0 42 | 43 | 44 | True 45 | 46 | 47 | TimerQueuesConsole\ 48 | TimerQueuesConsole 49 | ProjectOutput 50 | 1 51 | 52 | 53 | True 54 | True 55 | 56 | 57 | 58 | 59 | TimerQueuesConsole.app\Contents\MacOS\ 60 | libcgunwind.1.0.dylib 61 | DependencyModule 62 | 1 63 | 64 | 65 | True 66 | 67 | 68 | TimerQueuesConsole.app\Contents\MacOS\ 69 | libcgsqlite3.dylib 70 | DependencyModule 71 | 1 72 | 73 | 74 | True 75 | 76 | 77 | 78 | 79 | 80 | TimerQueuesConsole.app\ 81 | libPCRE.dylib 82 | DependencyModule 83 | 1 84 | 85 | 86 | True 87 | 88 | 89 | TimerQueuesConsole.app\ 90 | libcgunwind.1.0.dylib 91 | DependencyModule 92 | 1 93 | 94 | 95 | True 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Example/TimerQueuesConsole.dpr: -------------------------------------------------------------------------------- 1 | program TimerQueuesConsole; 2 | 3 | {$APPTYPE CONSOLE} 4 | 5 | {$R *.res} 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | Grijjy.TimerQueue; 11 | 12 | type 13 | TMyClass = class 14 | procedure OnTimer(const ASender: TObject); 15 | end; 16 | 17 | var 18 | TimerQueue: TgoTimerQueue; 19 | MyClass: TMyClass; 20 | 21 | { TMyClass } 22 | 23 | procedure TMyClass.OnTimer(const ASender: TObject); 24 | var 25 | S: String; 26 | Timer: TgoTimer; 27 | begin 28 | Timer := ASender as TgoTimer; 29 | S := (Format('OnTimer (%s, Thread=%s, Interval=%d)', 30 | [UInt32(Timer.Handle).ToHexString, TThread.CurrentThread.ThreadID.ToHexString, Timer.Interval])); 31 | 32 | Writeln(S); // The console is not thread safe 33 | end; 34 | 35 | begin 36 | ReportMemoryLeaksOnShutDown := True; 37 | 38 | try 39 | TimerQueue := TgoTimerQueue.Create; 40 | try 41 | MyClass := TMyClass.Create; 42 | try 43 | TimerQueue.Add(1000, MyClass.OnTimer); 44 | TimerQueue.Add(500, MyClass.OnTimer); 45 | TimerQueue.Add(100, MyClass.OnTimer); 46 | 47 | Readln; 48 | finally 49 | MyClass.Free; 50 | end; 51 | finally 52 | TimerQueue.Free; 53 | end; 54 | except 55 | on E: Exception do 56 | Writeln(E.ClassName, ': ', E.Message); 57 | end; 58 | end. 59 | -------------------------------------------------------------------------------- /Example/TimerQueuesConsole.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {D4EEB2CF-EF1B-4574-BF0C-1D301D87BB49} 4 | 18.4 5 | None 6 | TimerQueuesConsole.dpr 7 | True 8 | Debug 9 | Win64 10 | 130 11 | Console 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Base 34 | true 35 | 36 | 37 | true 38 | Base 39 | true 40 | 41 | 42 | true 43 | Base 44 | true 45 | 46 | 47 | true 48 | Base 49 | true 50 | 51 | 52 | true 53 | Base 54 | true 55 | 56 | 57 | true 58 | Base 59 | true 60 | 61 | 62 | true 63 | Cfg_1 64 | true 65 | true 66 | 67 | 68 | true 69 | Cfg_1 70 | true 71 | true 72 | 73 | 74 | true 75 | Base 76 | true 77 | 78 | 79 | .\$(Platform)\$(Config) 80 | .\Bin 81 | false 82 | false 83 | false 84 | false 85 | false 86 | RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) 87 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) 88 | TimerQueuesConsole 89 | ZMQLOGGING;$(DCC_Define) 90 | ..\..\GrijjyFoundation;$(DCC_UnitSearchPath) 91 | 92 | 93 | DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 94 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png 95 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png 96 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png 97 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png 98 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png 99 | $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png 100 | $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png 101 | $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png 102 | $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png 103 | android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar 104 | 105 | 106 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 107 | 108 | 109 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 110 | 111 | 112 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) 113 | 114 | 115 | FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;dbexpress;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;IndyProtocols;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) 116 | 117 | 118 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) 119 | true 120 | 121 | 122 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;svnui;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;Grijjy.Package.RTL;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;TMSListControlsDXE11;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;emshosting;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;Grijjy.Package.FMX;DataSnapServerMidas;$(DCC_UsePackage) 123 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 124 | Debug 125 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 126 | 1033 127 | true 128 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 129 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 130 | 131 | 132 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;emshosting;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) 133 | true 134 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 135 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 136 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) 137 | Debug 138 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 139 | 1033 140 | 141 | 142 | DEBUG;$(DCC_Define) 143 | true 144 | false 145 | true 146 | true 147 | true 148 | 149 | 150 | false 151 | 152 | 153 | 1033 154 | (None) 155 | 156 | 157 | false 158 | RELEASE;$(DCC_Define) 159 | 0 160 | 0 161 | 162 | 163 | 164 | MainSource 165 | 166 | 167 | Cfg_2 168 | Base 169 | 170 | 171 | Base 172 | 173 | 174 | Cfg_1 175 | Base 176 | 177 | 178 | 179 | Delphi.Personality.12 180 | Application 181 | 182 | 183 | 184 | TimerQueuesConsole.dpr 185 | 186 | 187 | Microsoft Office 2000 Sample Automation Server Wrapper Components 188 | Microsoft Office XP Sample Automation Server Wrapper Components 189 | 190 | 191 | 192 | 193 | 194 | true 195 | 196 | 197 | 198 | 199 | TimerQueuesConsole.exe 200 | true 201 | 202 | 203 | 204 | 205 | libzmq-linux64.so 206 | true 207 | 208 | 209 | 210 | 211 | libzmq-linux64.so 212 | true 213 | 214 | 215 | 216 | 217 | true 218 | 219 | 220 | 221 | 222 | true 223 | 224 | 225 | 226 | 227 | true 228 | 229 | 230 | 231 | 232 | TimerQueuesConsole 233 | true 234 | 235 | 236 | 237 | 238 | 1 239 | 240 | 241 | Contents\MacOS 242 | 1 243 | 244 | 245 | Contents\MacOS 246 | 0 247 | 248 | 249 | 250 | 251 | classes 252 | 1 253 | 254 | 255 | 256 | 257 | library\lib\armeabi-v7a 258 | 1 259 | 260 | 261 | 262 | 263 | library\lib\armeabi 264 | 1 265 | 266 | 267 | 268 | 269 | library\lib\mips 270 | 1 271 | 272 | 273 | 274 | 275 | library\lib\armeabi-v7a 276 | 1 277 | 278 | 279 | 280 | 281 | res\drawable 282 | 1 283 | 284 | 285 | 286 | 287 | res\values 288 | 1 289 | 290 | 291 | 292 | 293 | res\drawable 294 | 1 295 | 296 | 297 | 298 | 299 | res\drawable-xxhdpi 300 | 1 301 | 302 | 303 | 304 | 305 | res\drawable-ldpi 306 | 1 307 | 308 | 309 | 310 | 311 | res\drawable-mdpi 312 | 1 313 | 314 | 315 | 316 | 317 | res\drawable-hdpi 318 | 1 319 | 320 | 321 | 322 | 323 | res\drawable-xhdpi 324 | 1 325 | 326 | 327 | 328 | 329 | res\drawable-small 330 | 1 331 | 332 | 333 | 334 | 335 | res\drawable-normal 336 | 1 337 | 338 | 339 | 340 | 341 | res\drawable-large 342 | 1 343 | 344 | 345 | 346 | 347 | res\drawable-xlarge 348 | 1 349 | 350 | 351 | 352 | 353 | 1 354 | 355 | 356 | Contents\MacOS 357 | 1 358 | 359 | 360 | 0 361 | 362 | 363 | 364 | 365 | Contents\MacOS 366 | 1 367 | .framework 368 | 369 | 370 | 0 371 | 372 | 373 | 374 | 375 | 1 376 | .dylib 377 | 378 | 379 | 1 380 | .dylib 381 | 382 | 383 | 1 384 | .dylib 385 | 386 | 387 | Contents\MacOS 388 | 1 389 | .dylib 390 | 391 | 392 | 0 393 | .dll;.bpl 394 | 395 | 396 | 397 | 398 | 1 399 | .dylib 400 | 401 | 402 | 1 403 | .dylib 404 | 405 | 406 | 1 407 | .dylib 408 | 409 | 410 | Contents\MacOS 411 | 1 412 | .dylib 413 | 414 | 415 | 0 416 | .bpl 417 | 418 | 419 | 420 | 421 | 0 422 | 423 | 424 | 0 425 | 426 | 427 | 0 428 | 429 | 430 | 0 431 | 432 | 433 | Contents\Resources\StartUp\ 434 | 0 435 | 436 | 437 | 0 438 | 439 | 440 | 441 | 442 | 1 443 | 444 | 445 | 1 446 | 447 | 448 | 1 449 | 450 | 451 | 452 | 453 | 1 454 | 455 | 456 | 1 457 | 458 | 459 | 1 460 | 461 | 462 | 463 | 464 | 1 465 | 466 | 467 | 1 468 | 469 | 470 | 1 471 | 472 | 473 | 474 | 475 | 1 476 | 477 | 478 | 1 479 | 480 | 481 | 1 482 | 483 | 484 | 485 | 486 | 1 487 | 488 | 489 | 1 490 | 491 | 492 | 1 493 | 494 | 495 | 496 | 497 | 1 498 | 499 | 500 | 1 501 | 502 | 503 | 1 504 | 505 | 506 | 507 | 508 | 1 509 | 510 | 511 | 1 512 | 513 | 514 | 1 515 | 516 | 517 | 518 | 519 | 1 520 | 521 | 522 | 523 | 524 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 525 | 1 526 | 527 | 528 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 529 | 1 530 | 531 | 532 | 533 | 534 | 1 535 | 536 | 537 | 1 538 | 539 | 540 | 541 | 542 | ..\ 543 | 1 544 | 545 | 546 | ..\ 547 | 1 548 | 549 | 550 | 551 | 552 | 1 553 | 554 | 555 | 1 556 | 557 | 558 | 1 559 | 560 | 561 | 562 | 563 | 1 564 | 565 | 566 | 1 567 | 568 | 569 | 1 570 | 571 | 572 | 573 | 574 | ..\ 575 | 1 576 | 577 | 578 | 579 | 580 | Contents 581 | 1 582 | 583 | 584 | 585 | 586 | Contents\Resources 587 | 1 588 | 589 | 590 | 591 | 592 | library\lib\armeabi-v7a 593 | 1 594 | 595 | 596 | 1 597 | 598 | 599 | 1 600 | 601 | 602 | 1 603 | 604 | 605 | 1 606 | 607 | 608 | Contents\MacOS 609 | 1 610 | 611 | 612 | 0 613 | 614 | 615 | 616 | 617 | 1 618 | 619 | 620 | 1 621 | 622 | 623 | 624 | 625 | Assets 626 | 1 627 | 628 | 629 | Assets 630 | 1 631 | 632 | 633 | 634 | 635 | Assets 636 | 1 637 | 638 | 639 | Assets 640 | 1 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | False 654 | False 655 | False 656 | False 657 | True 658 | False 659 | False 660 | True 661 | 662 | 663 | 12 664 | 665 | 666 | 667 | 668 | 669 | -------------------------------------------------------------------------------- /Example/TimerQueuesConsole.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grijjy/DelphiPlatformTimerQueue/6a45dcafa79dffaa6a17c671e6ede385fce51deb/Example/TimerQueuesConsole.res -------------------------------------------------------------------------------- /Example/info.plist.TemplateOSX.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%VersionInfoPListKeys%> 6 | <%ExtraInfoPListKeys%> 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/info.plist.TemplateiOS.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%VersionInfoPListKeys%> 6 | <%ExtraInfoPListKeys%> 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using the Apple's Grand Central Dispatch and Android's ScheduledThreadPoolExecutor for Delphi timers 2 | You are probably already familiar with the NSTimer on iOS/macOS and JTimer on Android for timer events. In addition to the basic timers, most operating systems offer a more advanced threaded schedule event API. On Android there is the [ScheduledThreadPoolExecutor](https://developer.android.com/reference/java/util/concurrent/ScheduledThreadPoolExecutor) which allows you to launch a Runnable at a specific time period. On iOS/macOS the operating system includes the [Grand Central Dispatch](https://developer.apple.com/documentation/DISPATCH) (GCD) which allows you to schedule events. On Windows we have the CreateTimerQueueTimer APIs for a similar purpose. 3 | 4 | In this article we will show how you can use these APIs on mobile, desktop and server platforms in Delphi in a unified manner and receive OnTimer() events that you are already familiar with. 5 | 6 | Of course, it is possible to create your own thread pool and simulate timer events. However, the operating system already has it's own kernel-managed thread pool for scheduled events. By leveraging these APIs you can share thread resources across all the processes on the device and this is especially important for platforms where it is not advisable to create numerous threads. Unlike a regular timer object, you can run more than one scheduled timer from the same timer object, thereby avoiding allocating numerous individual timer objects. Timer events can share threads from the OS pool if the event durations don't exceed the interval rate and more. 7 | 8 | Also, there are situations where you need a scheduled timer but you don't have a main application loop to fire events or a window, such as library modules, server logic, etc. You can go through the effort of creating hidden windows of course, but the OS based scheduled event APIs don't have this limitation. 9 | 10 | - On Android we use the JScheduledThreadPoolExecutor class and JRunnable to allow the OS to manage the thread pool 11 | - On macOS/iOS we use the Grand Central Dispatch and allow the OS the OS to manage the thread pool 12 | - On Windows we use the CreateTimerQueueTimer() API to allow the OS/kernel to manage the thread pool and callback. 13 | - On Linux64 we use Epoll and the TimerFd capability to signal timer events along with our own managed thread pool 14 | 15 | For more information about us, our support and services visit the [Grijjy homepage](http://www.grijjy.com) or the [Grijjy developers blog](http://blog.grijjy.com). 16 | 17 | The example contained here depends upon part of our [Grijjy Foundation library](https://github.com/grijjy/GrijjyFoundation). 18 | 19 | The source code and related example repository is hosted on GitHub at [https://github.com/grijjy/DelphiPlatformTimerQueue](https://github.com/grijjy/DelphiPlatformTimerQueue). 20 | 21 | ## Grand Central Dispatch on iOS and macOS 22 | On iOS and macOS we have a unified approach called the [Grand Central Dispatch](https://developer.apple.com/documentation/DISPATCH). The GCD provides numerous capabilities to help developers with parallel applications on Apple devices. Apple recommends this approach for threading events so that all applications on a given device can better share system resources. 23 | 24 | To create a event that repeats at a specified interval, you need to use 5 core APIs of the GCD: 25 | 1. Use the `dispatch_get_global_queue()` API to create a global queue that you will share with all your timers. 26 | 2. Call `dispatch_source_create()` to create a timer that is associated with the queue. 27 | 3. Call `dispatch_source_set_timer()` to specify the interval and accuracy of your timer. 28 | 4. Call `dispatch_source_set_event_handler()` to specify the callback for the event. 29 | 5. Finally call `dispatch_resume()` to start your timer. 30 | 31 | The APIs are straightforward to use, except for a couple of things. First, Delphi is currently missing some of these constants and exports, so we include a conversion called Macapi.Gcd.pas in our Grijjy Repository. 32 | 33 | Secondly, and far more difficult is the usage of the `dispatch_source_set_event_handler()` API which uses an ObjectiveC block for the callback. There are numerous approaches to using ObjC blocks in Delphi code, but we prefer a method implemented by the team over at Tamosoft. We include a unit to simplify those ObjC blocks in Delphi based upon their [blog article](https://habr.com/post/325204/). Their methodology makes the usage of an ObjC block in Delphi as simple as the following example, 34 | 35 | ```Delphi 36 | TObjCBlock.CreateBlockWithProcedure( 37 | procedure(p1: NSInteger; p2: Pointer) 38 | begin 39 | if Assigned(FOnTimer) then 40 | FOnTimer(Self); 41 | end)); 42 | ``` 43 | Here our anonymous method runs when the block is called which in turn calls our timer event. 44 | 45 | We only need to create a single global queue for all the GCD timers, so in Delphi we could use a simple method to check for the existance of the global queue, and create one if required. For example, 46 | ```Delphi 47 | function GetGlobalQueue: dispatch_queue_t; 48 | begin 49 | if FGlobalQueue = nil then 50 | FGlobalQueue := dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 51 | Result := FGlobalQueue; 52 | end; 53 | ``` 54 | 55 | Then to finally put it all together we create our timer using `DISPATCH_SOURCE_TYPE_TIMER` and use the `dispatch_source_set_timer` API to specify both the startup delay and the interval. We could choose to have the timer fire immediately upon startup, but it is customary to have the first event after the first interval, so we provide a delay which matches the interval. If you are concerned with accuracy of the timer, the GCD API also provides an accuracy leeway. 56 | 57 | Then we call `dispatch_source_set_event_handler` with our OnTimer() event as an ObjC block followed by `dispatch_resume` to start the timer. 58 | 59 | ```Delphi 60 | FDispatchTimer := dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, FTimerQueue.GlobalQueue); 61 | if Assigned(FDispatchTimer) then 62 | begin 63 | dispatch_source_set_timer(FDispatchTimer, 64 | dispatch_time(DISPATCH_TIME_NOW, AInterval * NSEC_PER_MSEC), // Start delay 65 | AInterval * NSEC_PER_MSEC, // Interval 66 | 0); // Leeway 67 | 68 | dispatch_source_set_event_handler(FDispatchTimer, 69 | TObjCBlock.CreateBlockWithProcedure( 70 | procedure(p1: NSInteger; p2: Pointer) 71 | begin 72 | if Assigned(FOnTimer) then 73 | FOnTimer(Self); 74 | end)); 75 | 76 | dispatch_resume(FDispatchTimer); 77 | ``` 78 | 79 | To change the interval rate of the timer, while the timer is operating the GCD you simply call the `dispatch_source_set_timer` method again: 80 | 81 | ```Delphi 82 | dispatch_source_set_timer(FDispatchTimer, 83 | dispatch_time(DISPATCH_TIME_NOW, AInterval * NSEC_PER_MSEC), AInterval * NSEC_PER_MSEC, 0); 84 | ``` 85 | 86 | ## ScheduledThreadPoolExecutor for Android 87 | On Android we have a class called the ScheduledThreadPoolExecutor that allows us to create timers using an API called `scheduleAtFixedRate`. 88 | 89 | To create a ScheduledThreadPoolExecutor we simple define one and initialize it. During the initialization we must specify the total maximum threads in the pool. 90 | 91 | ```Delphi 92 | var 93 | ScheduledThreadPoolExecutor: JScheduledThreadPoolExecutor; 94 | 95 | ScheduledThreadPoolExecutor := _TJScheduledThreadPoolExecutor.JavaClass.init(ANDROID_THREAD_POOL_SIZE); 96 | 97 | ``` 98 | The ScheduledThreadPoolExecutor expects to call a Runnable object, so to use this API we must first create a Runnable object. The following example shows a simple example of how this is done: 99 | ```Delphi 100 | var 101 | FRunnable: JRunnable; 102 | 103 | type 104 | TAndroidRunnable = class(TJavaLocal, JRunnable) 105 | private 106 | FTimer: TgoTimer; 107 | public 108 | constructor Create(const ATimer: TgoTimer); 109 | procedure run; cdecl; 110 | end; 111 | 112 | FRunnable := TAndroidRunnable.Create(Self); 113 | ``` 114 | Then to startup your timer you only need to call the `scheduleAtFixedRate` API. 115 | 116 | ```Delphi 117 | FScheduledFuture := ScheduledThreadPoolExecutor.scheduleAtFixedRate(FRunnable, AInterval, AInterval, TJTimeUnit.JavaClass.MILLISECONDS); 118 | ``` 119 | A few things to note here. First off, just like the Apple GCD, you are passing both an initial delay and an interval so the timer events start at the first interval. More importantly we are returning a ScheduledFuture object here. The ScheduledFuture allows you to interact with your timer. Delphi's import for the `scheduleAtFixedRate` API does not return a ScheduledFuture, so in our implementation we created another version of the import. 120 | 121 | To change the interval rate of a running timer you only need to cancel the existing timer, and call `scheduleAtFixedRate` again, for example: 122 | 123 | ```Delphi 124 | FScheduledFuture.cancel(True); 125 | FScheduledFuture := ScheduledThreadPoolExecutor.scheduleAtFixedRate(FRunnable, AInterval, AInterval, TJTimeUnit.JavaClass.MILLISECONDS); 126 | ``` 127 | ## Windows CreateTimerQueueTimer and Linux EPoll 128 | On Windows we have an API called `CreateTimerQueueTimer` to perform a substantially similar thread based timer. On Linux we have a concept of timer file descriptors which we can use with the Epoll APIs and our own thread pool to time events. 129 | 130 | Since we covered these concepts in detail for Windows and Linux timers in a [recent article](https://blog.grijjy.com/2017/04/20/cross-platform-timer-queues-for-windows-and-linux/), I won't go into detail in this article. Please refer to that article if your interest is primarily Windows or Linux. 131 | 132 | However, we have merged those concepts into a single unit and a unified class so that the TgoTimer() related unit and classes work the same on all platforms including iOS, macOS, Android, Windows and Linux. As a developer you can simply use the class and setup your OnTimer() events and the interface is identical. 133 | 134 | ## Considerations 135 | Please keep in mind that there are some differences between platforms in how they handle overlapped timer events (when your total callback time exceeds the interval rate). Some of the platform specific APIs will simply issue a new thread and timer event at the interval rate while others will wait until you return. 136 | 137 | If you don't want overlapping events, then one way to handle this is to check in your own OnTimer() event if you are already executing and exit. You could use an Atomic operation to check or a TryLock condition. 138 | 139 | Of course you may want your timer events to overlap, especially if your timer event is time sensitive. As a developer you have to consider your scenario. 140 | 141 | Also Delphi native timers are designed to be synchronized with the main application thread. This allows you to update the user interface from your timer event. Your normal Delphi timer will be blocked in cases that the operation takes longer than the interval and this is sometimes desired behavior. 142 | 143 | If you want to synchronize the TgoTimer event with the application thread, you could simply call TThread.Synchronize in your OnTimer() event, 144 | 145 | ```Delphi 146 | TThread.Synchronize(nil, 147 | procedure 148 | begin 149 | // Update the user interface 150 | end); 151 | ``` 152 | We do this in our Firemonkey example so that we can update a Memo component from our OnTimer() event. 153 | 154 | ## Examples and source code 155 | In our GitHub repository we have included both a cross-platform Firemonkey based example that runs on all platforms to demonstrate timer basics and a console application for Windows and Linux. 156 | 157 | The example program at [https://github.com/grijjy/DelphiPlatformTimerQueue](https://github.com/grijjy/DelphiPlatformTimerQueue). 158 | 159 | ## Conclusion 160 | In the end it's not a lot of code to unify timers on all platforms, but it took us a while to figure out these nuances on mobile platforms. We hope you find this article helpful for your efforts and a useful addition to your application. We have utilized threaded timers for silent reconnection logic, heartbeat logic on servers and more. 161 | 162 | For more information about us, our support and services visit the [Grijjy homepage](http://www.grijjy.com) or the [Grijjy developers blog](http://blog.grijjy.com). 163 | 164 | The base classes described herein are part of our [Grijjy Foundation library](https://github.com/grijjy/GrijjyFoundation). --------------------------------------------------------------------------------