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
22 #include "../pith/debug.h"
23 #include "../pith/osdep/err_desc.h"
25 #include "../pico/utf8stub.h"
33 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
34 static pthread_t after_thread
;
35 static pthread_mutex_t status_message_mutex
;
39 /* internal prototypes */
40 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
41 void *do_after(void *);
46 void cleanup_after(void *);
50 * start_after - pause and/or loop calling passed function
51 * without getting in the way of main thread
55 start_after(AFTER_S
*a
)
58 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
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
);
74 if((rc
= pthread_create(&after_thread
, &attr
, do_after
, (void *) a
)) != 0){
76 dprint((1, "start_after: pthread_create failed %d (%d)", rc
, errno
));
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
88 (void) (*a
->f
)(a
->data
); /* do the thing */
90 cleanup_data
= (void *) a
;
98 * stop_after - stop the thread
103 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
106 dprint((9, "stop_after(join=%d) tid=%x", join
, pthread_self()));
109 if((rv
= pthread_cancel(after_thread
)) != 0){ /* tell thread to end */
110 dprint((1, "pthread_cancel: %d (%s)\n", rv
, error_description(errno
)));
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 */
126 #else /* !(defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)) */
128 cleanup_after((void *) cleanup_data
);
136 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
138 * do_after - loop thru list of pause/loop functions
148 #if defined(SIGCHLD) || defined(SIGWINCH)
151 /* make sure we don't end up with SIGCHLD */
152 sigaddset(&sigs
, SIGCHLD
);
155 /* or with SIGWINCH */
156 sigaddset(&sigs
, SIGWINCH
);
157 #endif /* SIGWINCH */
158 pthread_sigmask(SIG_BLOCK
, &sigs
, NULL
);
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
);
167 for(a
= (AFTER_S
*) data
; a
!= NULL
&& a
->f
!= NULL
; a
= a
->next
){
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 */
177 /* after waking, make sure we're still wanted */
178 pthread_testcancel();
180 loop
= (*a
->f
)(a
->data
); /* do the thing */
183 ts
.tv_sec
= loop
/ 100;
184 ts
.tv_nsec
= (loop
% 100) * 10000000;
186 if(nanosleep(&ts
, NULL
))
187 pthread_exit(NULL
); /* interrupted */
194 pthread_cleanup_pop(1);
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
206 cleanup_after(void *data
)
210 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
211 dprint((9, "cleanup_after() tid=%x", pthread_self()));
214 /* free linked list of AFTER_S's */
215 a
= (AFTER_S
*) data
;
230 new_afterstruct(void)
234 if((a
= (AFTER_S
*)malloc(sizeof(AFTER_S
))) == NULL
){
235 fatal("Out of memory");
238 memset((void *) a
, 0, sizeof(AFTER_S
));
245 status_message_lock_init(void)
247 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
248 pthread_mutex_init(&status_message_mutex
, NULL
);
254 status_message_lock(void)
256 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
257 return(pthread_mutex_lock(&status_message_mutex
));
265 status_message_unlock(void)
267 #if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
268 return(pthread_mutex_unlock(&status_message_mutex
));