doxygen: add newline after \file
[blender.git] / source / blender / blenlib / intern / BLI_timer.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2018 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bli
22  */
23
24 #include "BLI_timer.h"
25 #include "BLI_listbase.h"
26 #include "BLI_callbacks.h"
27
28 #include "MEM_guardedalloc.h"
29 #include "PIL_time.h"
30
31 #define GET_TIME() PIL_check_seconds_timer()
32
33 typedef struct TimedFunction {
34         struct TimedFunction *next, *prev;
35         BLI_timer_func func;
36         BLI_timer_data_free user_data_free;
37         void *user_data;
38         double next_time;
39         uintptr_t uuid;
40         bool tag_removal;
41         bool persistent;
42 } TimedFunction;
43
44 typedef struct TimerContainer {
45         ListBase funcs;
46         bool file_load_cb_registered;
47 } TimerContainer;
48
49 static TimerContainer GlobalTimer = {{0}};
50
51 static void ensure_callback_is_registered(void);
52
53 void BLI_timer_register(
54         uintptr_t uuid,
55         BLI_timer_func func,
56         void *user_data,
57         BLI_timer_data_free user_data_free,
58         double first_interval,
59         bool persistent)
60 {
61         ensure_callback_is_registered();
62
63         TimedFunction *timed_func = MEM_callocN(sizeof(TimedFunction), __func__);
64         timed_func->func = func;
65         timed_func->user_data_free = user_data_free;
66         timed_func->user_data = user_data;
67         timed_func->next_time = GET_TIME() + first_interval;
68         timed_func->tag_removal = false;
69         timed_func->persistent = persistent;
70         timed_func->uuid = uuid;
71
72         BLI_addtail(&GlobalTimer.funcs, timed_func);
73 }
74
75 static void clear_user_data(TimedFunction *timed_func)
76 {
77         if (timed_func->user_data_free) {
78                 timed_func->user_data_free(timed_func->uuid, timed_func->user_data);
79                 timed_func->user_data_free = NULL;
80         }
81 }
82
83 bool BLI_timer_unregister(uintptr_t uuid)
84 {
85         LISTBASE_FOREACH(TimedFunction *, timed_func, &GlobalTimer.funcs) {
86                 if (timed_func->uuid == uuid) {
87                         if (timed_func->tag_removal) {
88                                 return false;
89                         }
90                         else {
91                                 timed_func->tag_removal = true;
92                                 clear_user_data(timed_func);
93                                 return true;
94                         }
95                 }
96         }
97         return false;
98 }
99
100 bool BLI_timer_is_registered(uintptr_t uuid)
101 {
102         LISTBASE_FOREACH(TimedFunction *, timed_func, &GlobalTimer.funcs) {
103                 if (timed_func->uuid == uuid && !timed_func->tag_removal) {
104                         return true;
105                 }
106         }
107         return false;
108 }
109
110 static void execute_functions_if_necessary(void)
111 {
112         double current_time = GET_TIME();
113
114         LISTBASE_FOREACH(TimedFunction *, timed_func, &GlobalTimer.funcs) {
115                 if (timed_func->tag_removal) continue;
116                 if (timed_func->next_time > current_time) continue;
117
118                 double ret = timed_func->func(timed_func->uuid, timed_func->user_data);
119
120                 if (ret < 0) {
121                         timed_func->tag_removal = true;
122                 }
123                 else {
124                         timed_func->next_time = current_time + ret;
125                 }
126         }
127 }
128
129 static void remove_tagged_functions(void)
130 {
131         for (TimedFunction *timed_func = GlobalTimer.funcs.first; timed_func; ) {
132                 TimedFunction *next = timed_func->next;
133                 if (timed_func->tag_removal) {
134                         clear_user_data(timed_func);
135                         BLI_freelinkN(&GlobalTimer.funcs, timed_func);
136                 }
137                 timed_func = next;
138         }
139 }
140
141 void BLI_timer_execute()
142 {
143         execute_functions_if_necessary();
144         remove_tagged_functions();
145 }
146
147 void BLI_timer_free()
148 {
149         LISTBASE_FOREACH(TimedFunction *, timed_func, &GlobalTimer.funcs) {
150                 timed_func->tag_removal = true;
151         }
152
153         remove_tagged_functions();
154 }
155
156 struct ID;
157 struct Main;
158 static void remove_non_persistent_functions(struct Main *UNUSED(_1), struct ID *UNUSED(_2), void *UNUSED(_3))
159 {
160         LISTBASE_FOREACH(TimedFunction *, timed_func, &GlobalTimer.funcs) {
161                 if (!timed_func->persistent) {
162                         timed_func->tag_removal = true;
163                 }
164         }
165 }
166
167 static bCallbackFuncStore load_post_callback = {
168         NULL, NULL, /* next, prev */
169         remove_non_persistent_functions, /* func */
170         NULL, /* arg */
171         0 /* alloc */
172 };
173
174 static void ensure_callback_is_registered()
175 {
176         if (!GlobalTimer.file_load_cb_registered) {
177                 BLI_callback_add(&load_post_callback, BLI_CB_EVT_LOAD_POST);
178                 GlobalTimer.file_load_cb_registered = true;
179         }
180 }