1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
/*
* The MIT License
*
* Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file psemaphore.h
* @brief Semaphore routines
* @author Alexander Saprykin
*
* A semaphore is a synchronization primitive which controls access to shared
* data from the concurrently running threads. Unlike a mutex (which is a
* particular case of a binary semaphore) it allows concurrent access not to
* only the one thread.
*
* The semaphore has a counter which means the number of available resources
* (units). Before entering a critical section a thread must perform the so
* called P (acquire) operation: if the counter is positive it decrements the
* counter by 1 and continues execution; otherwise the thread is suspended until
* the counter becomes positive. Before leaving the critical section the thread
* must perform the so called V (release) operation: increments the counter by 1
* and wakes up a waiting thread from the queue (if any).
*
* You can think about the semaphore as a resource controller: the P operation
* takes one unit, while the V operation gives one unit back. The thread could
* not continue execution without taking a resource unit. By setting the initial
* semaphore counter value you can control how much concurrent threads can work
* with a shared resource.
*
* This semaphore implementation is process-wide so you can synchronize not only
* the threads. But it makes this IPC primitive (actually like any other IPC
* primitive, as well) relatively heavy. Consider using a mutex or a spinlock
* instead if you do not need to cross a process boundary.
*
* A process-wide semaphore is identified by its name across the system, thus it
* is also called a named semaphore. Use p_semaphore_new() to open the named
* semaphore and p_semaphore_free() to close it.
*
* Please note the following platform specific differences:
*
* - Windows doesn't own IPC objects (processes own them), which means that a
* semaphore will be removed from the system after the last process or thread
* closes it (or after terminating all the processes and threads holding open
* semaphore).
*
* - UNIX systems own IPC objects. Because of that UNIX IPC objects can survive
* an application crash: an already used semaphore can be opened in a locked
* state and an application can fail into a deadlock or an inconsistent state.
* This could happen if you have not closed all the open semaphores explicitly
* before terminating the application.
*
* - IRIX allows to open several instances of a semaphore within the single
* process, but it will close the object after the first close call from any of
* the threads within the process.
*
* - AmigaOS has process-wide semaphores without actual tracking of counter,
* which means that semaphore behaves the same way as recursive mutex.
*
* - OpenVMS (as of 8.4 release) has declared prototypes for process-wide named
* semaphores but the actual implementation is broken.
*
* - OS/2 lacks support for process-wide named semaphores.
*
* - Syllable lacks support for process-wide named semaphores.
*
* - BeOS lacks support for process-wide named semaphores.
*
* Use the third argument as #P_SEM_ACCESS_CREATE in p_semaphore_new() to reset
* a semaphore value while opening it. This argument is ignored on Windows. You
* can also take ownership of the semaphore with p_semaphore_take_ownership() to
* explicitly remove it from the system after closing.
*/
#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
# error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
#endif
#ifndef PLIBSYS_HEADER_PSEMAPHORE_H
#define PLIBSYS_HEADER_PSEMAPHORE_H
#include <pmacros.h>
#include <ptypes.h>
#include <perror.h>
P_BEGIN_DECLS
/** Enum with semaphore creation modes. */
typedef enum PSemaphoreAccessMode_ {
P_SEM_ACCESS_OPEN = 0, /**< Opens an existing semaphore or creates one with a given value. */
P_SEM_ACCESS_CREATE = 1 /**< Creates semaphore, resets to a given value if exists. */
} PSemaphoreAccessMode;
/** Semaphore opaque data structure. */
typedef struct PSemaphore_ PSemaphore;
/**
* @brief Creates a new #PSemaphore object.
* @param name Semaphore name.
* @param init_val Initial semaphore value.
* @param mode Creation mode.
* @param[out] error Error report object, NULL to ignore.
* @return Pointer to a newly created #PSemaphore object in case of success,
* NULL otherwise.
* @since 0.0.1
*
* The @a init_val is used only in one of following cases: a semaphore with the
* such name doesn't exist, or the semaphore with the such name exists but
* @a mode specified as #P_SEM_ACCESS_CREATE (non-Windows platforms only). In
* other cases @a init_val is ignored. The @a name is system-wide, so any other
* process can open that semaphore passing the same name.
*/
P_LIB_API PSemaphore * p_semaphore_new (const pchar *name,
pint init_val,
PSemaphoreAccessMode mode,
PError **error);
/**
* @brief Takes ownership of a semaphore.
* @param sem Semaphore to take ownership.
* @since 0.0.1
*
* If you take ownership of a semaphore object, p_semaphore_free() will try to
* completely unlink it and remove from the system. This is useful on UNIX
* systems where the semaphore can survive an application crash. On the Windows
* platform this call has no effect.
*
* The common usage of this call is upon application startup to ensure that the
* semaphore from the previous crash will be unlinked from the system. To do
* that, call p_semaphore_new(), take ownership of the semaphore object and
* remove it with the p_semaphore_free() call. After that, create it again.
*
* You can also do the same thing upon semaphore creation passing
* #P_SEM_ACCESS_CREATE to p_semaphore_new(). The only difference is that you
* should already know whether this semaphore object is from the previous crash
* or not.
*/
P_LIB_API void p_semaphore_take_ownership (PSemaphore *sem);
/**
* @brief Acquires (P operation) a semaphore.
* @param sem #PSemaphore to acquire.
* @param[out] error Error report object, NULL to ignore.
* @return TRUE in case of success, FALSE otherwise.
* @since 0.0.1
*/
P_LIB_API pboolean p_semaphore_acquire (PSemaphore *sem,
PError **error);
/**
* @brief Releases (V operation) a semaphore.
* @param sem #PSemaphore to release.
* @param[out] error Error report object, NULL to ignore.
* @return TRUE in case of success, FALSE otherwise.
* @since 0.0.1
*/
P_LIB_API pboolean p_semaphore_release (PSemaphore *sem,
PError **error);
/**
* @brief Frees #PSemaphore object.
* @param sem #PSemaphore to free.
* @since 0.0.1
*
* It doesn't release an acquired semaphore, be careful to not to make a
* deadlock while removing the acquired semaphore.
*/
P_LIB_API void p_semaphore_free (PSemaphore *sem);
P_END_DECLS
#endif /* PLIBSYS_HEADER_PSEMAPHORE_H */
|