* New version 2.26
[alpine.git] / alpine / after.c
blob7d5cbe55ab1c43d7bedf83a5c3621f5d954b81e8
1 /* ========================================================================
2 * Copyright 2013-2022 Eduardo Chappa
3 * Copyright 2006-2007 University of Washington
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
14 /*======================================================================
15 Implement asynchronous start_after() call
16 ====*/
19 #include <system.h>
20 #include <general.h>
22 #include "../pith/debug.h"
23 #include "../pith/osdep/err_desc.h"
25 #include "../pico/utf8stub.h"
27 #include "after.h"
30 /* internal state */
31 int after_active;
33 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
34 static pthread_t after_thread;
35 static pthread_mutex_t status_message_mutex;
36 #endif
39 /* internal prototypes */
40 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
41 void *do_after(void *);
42 #else
43 void *cleanup_data;
44 #endif
46 void cleanup_after(void *);
50 * start_after - pause and/or loop calling passed function
51 * without getting in the way of main thread
54 void
55 start_after(AFTER_S *a)
57 if(a){
58 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
59 pthread_attr_t attr;
60 int rc;
61 size_t stack;
63 if(after_active)
64 stop_after(1);
66 /* Initialize and set thread detached attribute */
67 pthread_attr_init(&attr);
68 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
69 #if defined(PTHREAD_STACK_MIN)
70 stack = PTHREAD_STACK_MIN + 0x10000;
71 pthread_attr_setstacksize(&attr, stack);
72 #endif
74 if((rc = pthread_create(&after_thread, &attr, do_after, (void *) a)) != 0){
75 after_active = 0;
76 dprint((1, "start_after: pthread_create failed %d (%d)", rc, errno));
78 else
79 after_active = 1;
81 pthread_attr_destroy(&attr);
82 dprint((9, "start_after() created %x: done", after_thread));
83 #else /* !(defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)) */
85 * Just call the first function
87 if(!a->delay)
88 (void) (*a->f)(a->data); /* do the thing */
90 cleanup_data = (void *) a;
91 after_active = 1;
92 #endif
98 * stop_after - stop the thread
100 void
101 stop_after(int join)
103 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
104 int rv;
106 dprint((9, "stop_after(join=%d) tid=%x", join, pthread_self()));
108 if(after_active){
109 if((rv = pthread_cancel(after_thread)) != 0){ /* tell thread to end */
110 dprint((1, "pthread_cancel: %d (%s)\n", rv, error_description(errno)));
113 if(join){
114 if((rv = pthread_join(after_thread, NULL)) != 0){ /* wait for it to end */
115 dprint((1, "pthread_join: %d (%s)\n", rv, error_description(errno)));
118 else if((rv = pthread_detach(after_thread)) != 0){ /* mark thread for deletion */
119 dprint((1, "pthread_detach: %d (%s)\n", rv, error_description(errno)));
123 /* not literally true unless "join" set */
124 after_active = 0;
126 #else /* !(defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)) */
128 cleanup_after((void *) cleanup_data);
129 cleanup_data = NULL;
130 after_active = 0;
132 #endif
136 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
138 * do_after - loop thru list of pause/loop functions
140 void *
141 do_after(void *data)
143 AFTER_S *a;
144 struct timespec ts;
145 int loop;
146 sigset_t sigs;
148 #if defined(SIGCHLD) || defined(SIGWINCH)
149 sigemptyset(&sigs);
150 #if defined(SIGCHLD)
151 /* make sure we don't end up with SIGCHLD */
152 sigaddset(&sigs, SIGCHLD);
153 #endif /* SIGCHLD */
154 #if defined(SIGCHLD)
155 /* or with SIGWINCH */
156 sigaddset(&sigs, SIGWINCH);
157 #endif /* SIGWINCH */
158 pthread_sigmask(SIG_BLOCK, &sigs, NULL);
159 #endif
161 /* prepare for the finish */
162 pthread_cleanup_push(cleanup_after, data);
163 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
164 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
166 /* and jump in */
167 for(a = (AFTER_S *) data; a != NULL && a->f != NULL; a = a->next){
168 if(a->delay){
169 ts.tv_sec = a->delay / 100; /* seconds */
170 ts.tv_nsec = (a->delay % 100) * 10000000;
172 if(nanosleep(&ts, NULL))
173 pthread_exit(NULL); /* interrupted */
176 while(1){
177 /* after waking, make sure we're still wanted */
178 pthread_testcancel();
180 loop = (*a->f)(a->data); /* do the thing */
182 if(loop > 0){
183 ts.tv_sec = loop / 100;
184 ts.tv_nsec = (loop % 100) * 10000000;
186 if(nanosleep(&ts, NULL))
187 pthread_exit(NULL); /* interrupted */
189 else
190 break;
194 pthread_cleanup_pop(1);
195 pthread_exit(NULL);
198 #endif /* defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP) */
202 * cleanup_after - give start_after caller opportunity to clean up
203 * their data, then free up AFTER_S list
205 void
206 cleanup_after(void *data)
208 AFTER_S *a, *an;
210 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
211 dprint((9, "cleanup_after() tid=%x", pthread_self()));
212 #endif
214 /* free linked list of AFTER_S's */
215 a = (AFTER_S *) data;
216 while(a != NULL){
217 an = a->next;
219 if(a->cf)
220 (*a->cf)(a->data);
222 free((void *) a);
224 a = an;
229 AFTER_S *
230 new_afterstruct(void)
232 AFTER_S *a;
234 if((a = (AFTER_S *)malloc(sizeof(AFTER_S))) == NULL){
235 fatal("Out of memory");
238 memset((void *) a, 0, sizeof(AFTER_S));
240 return(a);
244 void
245 status_message_lock_init(void)
247 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
248 pthread_mutex_init(&status_message_mutex, NULL);
249 #endif
254 status_message_lock(void)
256 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
257 return(pthread_mutex_lock(&status_message_mutex));
258 #else
259 return(0);
260 #endif
265 status_message_unlock(void)
267 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
268 return(pthread_mutex_unlock(&status_message_mutex));
269 #else
270 return(0);
271 #endif