xref: /haiku/src/system/libroot/posix/pthread/pthread_barrier.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /*
2  * Copyright 2016, Dmytro Shynkevych, dm.shynk@gmail.com
3  * Distributed under the terms of the MIT license.
4  */
5 
6 
7 #include <pthread.h>
8 #include "pthread_private.h"
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <syscall_utils.h>
15 #include <syscalls.h>
16 #include <user_mutex_defs.h>
17 
18 
19 #define BARRIER_FLAG_SHARED	0x80000000
20 
21 
22 static const pthread_barrierattr pthread_barrierattr_default = {
23 	/* .process_shared = */ false
24 };
25 
26 
27 int
28 pthread_barrier_init(pthread_barrier_t* barrier,
29 	const pthread_barrierattr_t* _attr, unsigned count)
30 {
31 	const pthread_barrierattr* attr = _attr != NULL
32 		? *_attr : &pthread_barrierattr_default;
33 
34 	if (barrier == NULL || attr == NULL || count < 1)
35 		return B_BAD_VALUE;
36 
37 	barrier->flags = attr->process_shared ? BARRIER_FLAG_SHARED : 0;
38 	barrier->lock = 0;
39 	barrier->mutex = 0;
40 	barrier->waiter_count = 0;
41 	barrier->waiter_max = count;
42 
43 	return B_OK;
44 }
45 
46 
47 int
48 pthread_barrier_wait(pthread_barrier_t* barrier)
49 {
50 	if (barrier == NULL)
51 		return B_BAD_VALUE;
52 
53 	// Enter critical region: lock the mutex
54 	int32 status = atomic_or((int32*)&barrier->mutex, B_USER_MUTEX_LOCKED);
55 
56 	// If already locked, call the kernel
57 	if (status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) {
58 		do {
59 			status = _kern_mutex_lock((int32*)&barrier->mutex, NULL, 0, 0);
60 		} while (status == B_INTERRUPTED);
61 
62 		if (status != B_OK)
63 			return status;
64 	}
65 
66 	barrier->waiter_count++;
67 
68 	// If this thread is the last to arrive
69 	if (barrier->waiter_count == barrier->waiter_max) {
70 		// Let other threads exit the do...while loop
71 		barrier->waiter_count = 0;
72 
73 		// Wake up everyone trying to acquire the barrier lock
74 		_kern_mutex_unlock((int32*)&barrier->lock, B_USER_MUTEX_UNBLOCK_ALL);
75 
76 		// Exit critical region: unlock the mutex
77 		int32 status = atomic_and((int32*)&barrier->mutex,
78 				~(int32)B_USER_MUTEX_LOCKED);
79 
80 		if (status & B_USER_MUTEX_WAITING)
81 			_kern_mutex_unlock((int32*)&barrier->mutex, 0);
82 
83 		// Inform the calling thread that it arrived last
84 		return PTHREAD_BARRIER_SERIAL_THREAD;
85 	}
86 
87 	do {
88 		// Wait indefinitely trying to acquire the barrier lock.
89 		// Other threads may now enter (mutex is unlocked).
90 		_kern_mutex_switch_lock((int32*)&barrier->mutex,
91 				(int32*)&barrier->lock, "barrier wait", 0, 0);
92 	} while (barrier->waiter_count != 0);
93 
94 	// This thread did not arrive last
95 	return 0;
96 }
97 
98 
99 int
100 pthread_barrier_destroy(pthread_barrier_t* barrier)
101 {
102 	// No dynamic resources to free
103 	return B_OK;
104 }
105 
106 
107 int
108 pthread_barrierattr_init(pthread_barrierattr_t* _attr)
109 {
110 	pthread_barrierattr* attr = (pthread_barrierattr*)malloc(
111 		sizeof(pthread_barrierattr));
112 
113 	if (attr == NULL)
114 		return B_NO_MEMORY;
115 
116 	*attr = pthread_barrierattr_default;
117 	*_attr = attr;
118 
119 	return B_OK;
120 }
121 
122 
123 int
124 pthread_barrierattr_destroy(pthread_barrierattr_t* _attr)
125 {
126 	pthread_barrierattr* attr = _attr != NULL ? *_attr : NULL;
127 
128 	if (attr == NULL)
129 		return B_BAD_VALUE;
130 
131 	free(attr);
132 
133 	return B_OK;
134 }
135 
136 
137 int
138 pthread_barrierattr_getpshared(const pthread_barrierattr_t* _attr, int* shared)
139 {
140 	pthread_barrierattr* attr;
141 
142 	if (_attr == NULL || (attr = *_attr) == NULL || shared == NULL)
143 		return B_BAD_VALUE;
144 
145 	*shared = attr->process_shared
146 		? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
147 
148 	return B_OK;
149 }
150 
151 
152 int
153 pthread_barrierattr_setpshared(pthread_barrierattr_t* _attr, int shared)
154 {
155 	pthread_barrierattr* attr;
156 
157 	if (_attr == NULL || (attr = *_attr) == NULL
158 		|| shared < PTHREAD_PROCESS_PRIVATE
159 		|| shared > PTHREAD_PROCESS_SHARED) {
160 		return B_BAD_VALUE;
161 	}
162 
163 	attr->process_shared = shared == PTHREAD_PROCESS_SHARED;
164 
165 	return 0;
166 }
167