summaryrefslogtreecommitdiffstats
path: root/once.c
diff options
context:
space:
mode:
Diffstat (limited to 'once.c')
-rw-r--r--once.c58
1 files changed, 58 insertions, 0 deletions
diff --git a/once.c b/once.c
new file mode 100644
index 0000000..de41be3
--- /dev/null
+++ b/once.c
@@ -0,0 +1,58 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <assert.h>
+
+#include "once.h"
+#include "mutex.h"
+#include "malloc.h"
+#include "macro.h"
+
+static sa_mutex_t *global_mutex;
+static pthread_once_t global_mutex_once = PTHREAD_ONCE_INIT;
+
+static void global_mutex_once_func(void) {
+ global_mutex = sa_mutex_new(0);
+}
+
+int sa_once(sa_once_t *control, sa_once_func_t func) {
+ int r;
+
+ assert(control);
+ assert(func);
+
+ /* Create the global mutex */
+ sa_assert_success(pthread_once(&global_mutex_once, global_mutex_once_func));
+
+ if (!global_mutex)
+ return -1;
+
+ r = 0;
+
+ /* Create the local mutex */
+ sa_mutex_lock(global_mutex);
+ if (!control->mutex) {
+ if (!(control->mutex = sa_mutex_new(1)))
+ r = -1;
+ }
+ sa_mutex_unlock(global_mutex);
+
+ if (!r)
+ return -1;
+
+ /* Execute function */
+ sa_mutex_lock(control->mutex);
+ if (!control->once_value) {
+ control->once_value = 1;
+ func();
+ }
+ sa_mutex_unlock(control->mutex);
+
+ /* Caveat: We have to make sure that the once func has completed
+ * before returning, even if the once func is not actually
+ * executed by us. Hence the awkward locking. */
+
+ return 0;
+}