Objectively
Object oriented framework for C.
Loading...
Searching...
No Matches
ThreadPool.c
Go to the documentation of this file.
1/*
2 * Objectively: Ultra-lightweight object oriented framework for GNU C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include "Config.h"
25
26#include <assert.h>
27#include <stdlib.h>
28
29#if defined(_WIN32)
30#include <Windows.h>
31#endif
32
33#if HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
37#include "ThreadPool.h"
38
39#define _Class _ThreadPool
40
49
86
87#pragma mark - Object
88
92static Object *copy(const Object *self) {
93 return NULL;
94}
95
99static void dealloc(Object *self) {
100
101 ThreadPool *this = (ThreadPool *) self;
102
103 const Array *threads = this->threads;
104
105 synchronized(this->condition, {
106 for (size_t i = 0; i < threads->count; i++) {
107 $((Thread *) $(threads, objectAtIndex, i), cancel);
108 }
109 $(this->condition, broadcast);
110 });
111
112 for (size_t i = 0; i < threads->count; i++) {
113 $((Thread *) $(threads, objectAtIndex, i), join, NULL);
114 }
115
116 assert(this->head == NULL);
117
118 release(this->threads);
119
120 release(this->condition);
121
122 super(Object, self, dealloc);
123}
124
125#pragma mark - ThreadPool
126
131
132 Thread *thread = $(alloc(Thread), initWithFunction, work->function, work->data);
133 assert(thread);
134
135 work->result = work->function(thread);
137
138 release(thread);
139}
140
145static ThreadPoolWork *submit(ThreadPool *self, ThreadFunction function, ident data, bool isDetached) {
146
147 assert(function);
148
149 ThreadPoolWork *work = calloc(1, sizeof(ThreadPoolWork));
150 assert(work);
151
152 work->pool = self;
154 work->isDetached = isDetached;
155 work->function = function;
156 work->data = data;
157
158 if (self->size == 0) {
159 runOnCallingThread(work);
160 if (isDetached) {
161 free(work);
162 return NULL;
163 }
164 return work;
165 }
166
167 synchronized(self->condition, {
168 if (self->tail) {
169 self->tail->next = work;
170 } else {
171 self->head = work;
172 }
173 self->tail = work;
174 $(self->condition, broadcast);
175 });
176
177 return isDetached ? NULL : work;
178}
179
184static void detach(ThreadPool *self, ThreadFunction function, ident data) {
185 submit(self, function, data, true);
186}
187
193 return submit(self, function, data, false);
194}
195
200static ThreadPool *init(ThreadPool *self) {
201
202#if defined(_WIN32)
203 const size_t size = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
204#elif defined(_SC_NPROCESSORS_ONLN)
205 const long processors = sysconf(_SC_NPROCESSORS_ONLN);
206 const size_t size = processors > 0 ? (size_t) processors : 1;
207#else
208 const size_t size = 1;
209#endif
210
211 return $(self, initWithSize, size);
212}
213
218static void dequeue(ThreadPool *self, ThreadPoolWork *work) {
219
220 ThreadPoolWork *previous = NULL;
221 for (ThreadPoolWork *w = self->head; w; previous = w, w = w->next) {
222 if (w == work) {
223 if (previous) {
224 previous->next = work->next;
225 } else {
226 self->head = work->next;
227 }
228 if (self->tail == work) {
229 self->tail = previous;
230 }
231 work->next = NULL;
232 return;
233 }
234 }
235
236 assert(false);
237}
238
244static ident run(Thread *thread) {
245
246 ThreadPool *self = thread->data;
247 Condition *condition = self->condition;
248
249 $((Lock *) condition, lock);
250
251 while (true) {
252
253 while (self->head == NULL && $(thread, isCancelled) == false) {
254 $(condition, wait);
255 }
256
257 ThreadPoolWork *work = self->head;
258 if (work == NULL) {
259 break;
260 }
261
262 dequeue(self, work);
264
265 $((Lock *) condition, unlock);
266
267 thread->data = work->data;
268 const ident result = work->function(thread);
269
270 $((Lock *) condition, lock);
271
272 if (work->isDetached) {
273 free(work);
274 } else {
275 work->result = result;
277 $(condition, broadcast);
278 }
279 }
280
281 $((Lock *) condition, unlock);
282
283 return NULL;
284}
285
290static ThreadPool *initWithSize(ThreadPool *self, size_t size) {
291
292 self = (ThreadPool *) super(Object, self, init);
293 if (self) {
294
295 self->condition = $(alloc(Condition), init);
296 assert(self->condition);
297
298 self->size = size;
299
300 self->threads = $(alloc(Array), initWithCapacity, size);
301 assert(self->threads);
302
303 for (size_t i = 0; i < self->size; i++) {
304 Thread *thread = $(alloc(Thread), initWithFunction, run, self);
305 assert(thread);
306
307 $(self->threads, addObject, thread);
308 $(thread, start);
309
310 release(thread);
311 }
312 }
313
314 return self;
315}
316
321static ident _wait(ThreadPool *self, ThreadPoolWork *work) {
322
323 if (work == NULL) {
324 return NULL;
325 }
326
327 assert(work->pool == self);
328 assert(work->isDetached == false);
329
330 bool isPending = false;
331
332 synchronized(self->condition, {
333 if (work->state == ThreadPoolWorkPending) {
334 dequeue(self, work);
336 isPending = true;
337 } else {
338 while (work->state != ThreadPoolWorkFinished) {
339 $(self->condition, wait);
340 }
341 }
342 });
343
344 if (isPending) {
345 runOnCallingThread(work);
346 }
347
348 const ident result = work->result;
349 free(work);
350
351 return result;
352}
353
354#pragma mark - Class lifecycle
355
359static void initialize(Class *clazz) {
360
361 ((ObjectInterface *) clazz->interface)->copy = copy;
362 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
363
364 ((ThreadPoolInterface *) clazz->interface)->detach = detach;
365 ((ThreadPoolInterface *) clazz->interface)->execute = execute;
366 ((ThreadPoolInterface *) clazz->interface)->init = init;
367 ((ThreadPoolInterface *) clazz->interface)->initWithSize = initWithSize;
368 ((ThreadPoolInterface *) clazz->interface)->wait = _wait;
369}
370
376 static Class *clazz;
377 static Once once;
378
379 do_once(&once, {
380 clazz = _initialize(&(const ClassDef) {
381 .name = "ThreadPool",
382 .superclass = _Object(),
383 .instanceSize = sizeof(ThreadPool),
384 .interfaceSize = sizeof(ThreadPoolInterface),
385 .initialize = initialize,
386 });
387 });
388
389 return clazz;
390}
391
392#undef _Class
static ident objectAtIndex(const Array *self, size_t index)
Definition Array.c:578
static Array * initWithCapacity(Array *self, size_t capacity)
Definition Array.c:453
static void addObject(Array *self, const ident obj)
Definition Array.c:181
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition Class.c:356
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition Class.c:151
#define alloc(type)
Allocate and initialize and instance of type.
Definition Class.h:226
#define super(type, obj, method,...)
static void broadcast(Condition *self)
Definition Condition.c:57
static Data * data(void)
Definition Data.c:286
static void unlock(Lock *self)
Definition Lock.c:103
static void lock(Lock *self)
Definition Lock.c:81
Class * _Object(void)
Definition Object.c:136
static bool isCancelled(const Operation *self)
Definition Operation.c:140
static Operation * initWithFunction(Operation *self, OperationFunction function, ident data)
Definition Operation.c:125
static void start(Operation *self)
Definition Operation.c:185
static void cancel(Operation *self)
Definition Operation.c:73
static int head(RESTClient *self, const char *url, const char **headers)
Definition RESTClient.c:207
static void join(Thread *self, ident *status)
Definition Thread.c:139
ident(* ThreadFunction)(Thread *thread)
The function type for Thread execution.
Definition Thread.h:45
static void dequeue(ThreadPool *self, ThreadPoolWork *work)
Removes work from the queue.
Definition ThreadPool.c:218
static ThreadPoolWork * submit(ThreadPool *self, ThreadFunction function, ident data, bool isDetached)
Queues function, or runs it on the calling thread if this ThreadPool has no Threads.
Definition ThreadPool.c:145
static ThreadPoolWork * execute(ThreadPool *self, ThreadFunction function, ident data)
Definition ThreadPool.c:192
static ThreadPool * init(ThreadPool *self)
Definition ThreadPool.c:200
static void runOnCallingThread(ThreadPoolWork *work)
Runs work on the calling thread, with a Thread that is never started.
Definition ThreadPool.c:130
Class * _ThreadPool(void)
Definition ThreadPool.c:375
static ident run(Thread *thread)
ThreadFunction for the ThreadPool Threads.
Definition ThreadPool.c:244
static void dealloc(Object *self)
Definition ThreadPool.c:99
static void detach(ThreadPool *self, ThreadFunction function, ident data)
Definition ThreadPool.c:184
static ident _wait(ThreadPool *self, ThreadPoolWork *work)
Definition ThreadPool.c:321
static ThreadPool * initWithSize(ThreadPool *self, size_t size)
Definition ThreadPool.c:290
static Object * copy(const Object *self)
Definition ThreadPool.c:92
static void initialize(Class *clazz)
Definition ThreadPool.c:359
ThreadPoolWorkState
The states of a ThreadPoolWork.
Definition ThreadPool.c:44
@ ThreadPoolWorkPending
Definition ThreadPool.c:45
@ ThreadPoolWorkFinished
Definition ThreadPool.c:47
@ ThreadPoolWorkRunning
Definition ThreadPool.c:46
A fixed set of Threads that run ThreadFunctions on behalf of the caller.
void * ident
The identity type, similar to Objective-C id.
Definition Types.h:49
long Once
The Once type.
Definition Once.h:37
#define do_once(once, block)
Executes the given block at most one time.
Definition Once.h:43
Arrays.
Definition Array.h:56
size_t count
The count of elements.
Definition Array.h:72
ClassDefs are passed to _initialize via an archetype to initialize a Class.
Definition Class.h:41
The runtime representation of a Class.
Definition Class.h:90
ident interface
The interface of the Class.
Definition Class.h:100
POSIX Threads conditional variables.
Definition Condition.h:44
POSIX Threads locks.
Definition Lock.h:42
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46
void dealloc(Object *self)
Frees all resources held by this Object.
Definition Array.c:99
POSIX Threads.
Definition Thread.h:53
ident data
The user data.
Definition Thread.h:69
A fixed set of Threads that run ThreadFunctions on behalf of the caller.
Definition ThreadPool.h:64
ident wait(ThreadPool *self, ThreadPoolWork *work)
Waits for the specified ThreadPoolWork to finish, and frees it.
ThreadPool * initWithSize(ThreadPool *self, size_t size)
Initializes this ThreadPool with the specified number of Threads.
Definition ThreadPool.c:290
size_t size
The number of Threads.
Definition ThreadPool.h:93
An opaque handle to a ThreadFunction submitted with ThreadPool::execute.
Definition ThreadPool.c:50
bool isDetached
true if the Thread that runs this ThreadPoolWork frees it.
Definition ThreadPool.c:64
ident result
The return value of the ThreadFunction.
Definition ThreadPool.c:79
ThreadPoolWorkState state
The state.
Definition ThreadPool.c:84
ident data
The user data.
Definition ThreadPool.c:59
ThreadPoolWork * next
The next ThreadPoolWork in the queue, or NULL.
Definition ThreadPool.c:69
ThreadFunction function
The ThreadFunction.
Definition ThreadPool.c:54
ThreadPool * pool
The ThreadPool owning this work (weak reference).
Definition ThreadPool.c:74