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 
27 #include "MEM_guardedalloc.h"
28 #include "PIL_time.h"
29 
30 #define GET_TIME() PIL_check_seconds_timer()
31 
32 typedef struct TimedFunction {
33   struct TimedFunction *next, *prev;
34   BLI_timer_func func;
35   BLI_timer_data_free user_data_free;
36   void *user_data;
37   double next_time;
38   uintptr_t uuid;
39   bool tag_removal;
40   bool persistent;
41 } TimedFunction;
42 
43 typedef struct TimerContainer {
44   ListBase funcs;
45 } TimerContainer;
46 
47 static TimerContainer GlobalTimer = {{0}};
48 
BLI_timer_register(uintptr_t uuid,BLI_timer_func func,void * user_data,BLI_timer_data_free user_data_free,double first_interval,bool persistent)49 void BLI_timer_register(uintptr_t uuid,
50                         BLI_timer_func func,
51                         void *user_data,
52                         BLI_timer_data_free user_data_free,
53                         double first_interval,
54                         bool persistent)
55 {
56   TimedFunction *timed_func = MEM_callocN(sizeof(TimedFunction), __func__);
57   timed_func->func = func;
58   timed_func->user_data_free = user_data_free;
59   timed_func->user_data = user_data;
60   timed_func->next_time = GET_TIME() + first_interval;
61   timed_func->tag_removal = false;
62   timed_func->persistent = persistent;
63   timed_func->uuid = uuid;
64 
65   BLI_addtail(&GlobalTimer.funcs, timed_func);
66 }
67 
clear_user_data(TimedFunction * timed_func)68 static void clear_user_data(TimedFunction *timed_func)
69 {
70   if (timed_func->user_data_free) {
71     timed_func->user_data_free(timed_func->uuid, timed_func->user_data);
72     timed_func->user_data_free = NULL;
73   }
74 }
75 
BLI_timer_unregister(uintptr_t uuid)76 bool BLI_timer_unregister(uintptr_t uuid)
77 {
78   LISTBASE_FOREACH (TimedFunction *, timed_func, &GlobalTimer.funcs) {
79     if (timed_func->uuid == uuid && !timed_func->tag_removal) {
80       timed_func->tag_removal = true;
81       clear_user_data(timed_func);
82       return true;
83     }
84   }
85   return false;
86 }
87 
BLI_timer_is_registered(uintptr_t uuid)88 bool BLI_timer_is_registered(uintptr_t uuid)
89 {
90   LISTBASE_FOREACH (TimedFunction *, timed_func, &GlobalTimer.funcs) {
91     if (timed_func->uuid == uuid && !timed_func->tag_removal) {
92       return true;
93     }
94   }
95   return false;
96 }
97 
execute_functions_if_necessary(void)98 static void execute_functions_if_necessary(void)
99 {
100   double current_time = GET_TIME();
101 
102   LISTBASE_FOREACH (TimedFunction *, timed_func, &GlobalTimer.funcs) {
103     if (timed_func->tag_removal) {
104       continue;
105     }
106     if (timed_func->next_time > current_time) {
107       continue;
108     }
109 
110     double ret = timed_func->func(timed_func->uuid, timed_func->user_data);
111 
112     if (ret < 0) {
113       timed_func->tag_removal = true;
114     }
115     else {
116       timed_func->next_time = current_time + ret;
117     }
118   }
119 }
120 
remove_tagged_functions(void)121 static void remove_tagged_functions(void)
122 {
123   for (TimedFunction *timed_func = GlobalTimer.funcs.first; timed_func;) {
124     TimedFunction *next = timed_func->next;
125     if (timed_func->tag_removal) {
126       clear_user_data(timed_func);
127       BLI_freelinkN(&GlobalTimer.funcs, timed_func);
128     }
129     timed_func = next;
130   }
131 }
132 
BLI_timer_execute()133 void BLI_timer_execute()
134 {
135   execute_functions_if_necessary();
136   remove_tagged_functions();
137 }
138 
BLI_timer_free()139 void BLI_timer_free()
140 {
141   LISTBASE_FOREACH (TimedFunction *, timed_func, &GlobalTimer.funcs) {
142     timed_func->tag_removal = true;
143   }
144 
145   remove_tagged_functions();
146 }
147 
remove_non_persistent_functions(void)148 static void remove_non_persistent_functions(void)
149 {
150   LISTBASE_FOREACH (TimedFunction *, timed_func, &GlobalTimer.funcs) {
151     if (!timed_func->persistent) {
152       timed_func->tag_removal = true;
153     }
154   }
155 }
156 
BLI_timer_on_file_load(void)157 void BLI_timer_on_file_load(void)
158 {
159   remove_non_persistent_functions();
160 }
161