Objectively
Object oriented framework for C.
Loading...
Searching...
No Matches
Class.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 <dlfcn.h>
28#include <pthread.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <string.h>
32
33#if defined(_WIN32)
34#include <Windows.h>
35#endif
36
37#if HAVE_UNISTD_H
38#include <unistd.h>
39#endif
40
41#include "Class.h"
42#include "Object.h"
43
44size_t _pageSize;
45
47
52static pthread_mutex_t _classesLock = PTHREAD_MUTEX_INITIALIZER;
53
58typedef struct ClassImage ClassImage;
64
72
76static void teardown(void) {
77 Class *c;
78
79 c = _classes;
80 while (c) {
81 if (c->def.destroy) {
82 c->def.destroy(c);
83 }
84
85 c = c->next;
86 }
87
88 c = _classes;
89 while (c) {
90
91 Class *next = c->next;
92
93 free(c->interface);
94 free(c);
95
96 c = next;
97 }
98
100 while (i) {
101
102 ClassImage *next = i->next;
103
104 free(i);
105
106 i = next;
107 }
108}
109
113static void setup(void) {
114
115 _classes = NULL;
116
117#if !defined(_SC_PAGESIZE)
118 _pageSize = 4096;
119#else
120 _pageSize = sysconf(_SC_PAGESIZE);
121#endif
122
123 atexit(teardown);
124}
125
131static const ident imageForAddress(const ident address) {
132
133 assert(address);
134
135#if defined(_WIN32)
136 HMODULE module;
137 if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
138 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) address, &module)) {
139 return module;
140 }
141#else
142 Dl_info info;
143 if (dladdr(address, &info)) {
144 return info.dli_fbase;
145 }
146#endif
147
148 return NULL;
149}
150
152
153 static Once once;
154 do_once(&once, setup());
155
156 assert(def);
157 assert(def->name);
158 assert(def->instanceSize);
159 assert(def->interfaceSize);
160
161 Class *clazz = calloc(1, sizeof(Class));
162 assert(clazz);
163
164 clazz->def = *def;
165
166 clazz->interface = calloc(1, def->interfaceSize);
167 assert(clazz->interface);
168
169 Class *superclass = clazz->def.superclass;
170 if (superclass) {
171
172 assert(superclass->def.instanceSize <= def->instanceSize);
173 assert(superclass->def.interfaceSize <= def->interfaceSize);
174
175 memcpy(clazz->interface, superclass->interface, superclass->def.interfaceSize);
176 }
177
178 if (clazz->def.initialize) {
179 clazz->def.initialize(clazz);
180 }
181
182 /* def.name is a literal in the declaring image, where the ClassDef itself is
183 * a compound literal with automatic storage. */
184 clazz->image = imageForAddress((ident) def->name);
185
186 /* Taken here rather than around the whole function: def.initialize above can
187 * reach other archetypes, and so this, before that Class is published. */
188 pthread_mutex_lock(&_classesLock);
189
190 clazz->next = _classes;
191 _classes = clazz;
192
193 pthread_mutex_unlock(&_classesLock);
194
195 return clazz;
196}
197
199
200 ident obj = calloc(1, clazz->def.instanceSize);
201 assert(obj);
202
203 Object *object = (Object *) obj;
204
205 object->magic = OBJECTIVELY_MAGIC;
206 object->clazz = clazz;
207 object->referenceCount = 1;
208
209 return obj;
210}
211
212ident _cast(const Class *clazz, const ident obj) {
213
214 if (obj) {
215 const Class *c = ((Object *) obj)->clazz;
216 while (c) {
217
218 // as a special case, we optimize for _Object
219 if (c == clazz || clazz == _Object()) {
220 break;
221 }
222
223 c = c->def.superclass;
224 }
225 assert(c);
226 }
227
228 return (ident) obj;
229}
230
231void addClassImage(ident handle, const ident address) {
232
233 assert(handle);
234 assert(address);
235
236 const ident image = imageForAddress(address);
237 if (image == NULL) {
238 fprintf(stderr, "%s: no image contains %p\n", __func__, address);
239 abort();
240 }
241
242 ClassImage *classImage = calloc(1, sizeof(ClassImage));
243 assert(classImage);
244
245 classImage->handle = handle;
246 classImage->image = image;
247
248 /* Published the same way a Class is, and for the same reason: classForName
249 * walks this list on any thread. */
250 classImage->next = __atomic_load_n(&_images, __ATOMIC_RELAXED);
251 while (!__atomic_compare_exchange_n(&_images, &classImage->next, classImage, 1,
252 __ATOMIC_RELEASE, __ATOMIC_RELAXED)) ;
253}
254
256
257 assert(handle);
258
259 /* Held from the search to the last unlink, so that retiring an image and
260 * dropping its Classes is one operation, and a second call for the same handle
261 * finds it already gone. Nothing here reaches the loader. */
262 pthread_mutex_lock(&_classesLock);
263
264 ident image = NULL;
265
266 /* Retired in place rather than unlinked, so that a concurrent classForName
267 * parked on this node still has a next to follow, and never reads a node that
268 * has been freed. Retiring is a single store of the handle it matches on, so
269 * that walk sees this image or does not, and never half of it. Retired nodes
270 * are freed at teardown; reusing one would put a newly registered image where
271 * the retired one sat, and lookup order is newest first. */
272 for (ClassImage *i = __atomic_load_n(&_images, __ATOMIC_ACQUIRE); i; i = i->next) {
273 if (__atomic_load_n(&i->handle, __ATOMIC_ACQUIRE) == handle) {
274 image = i->image;
275 __atomic_store_n(&i->handle, NULL, __ATOMIC_RELEASE);
276 break;
277 }
278 }
279
280 if (image == NULL) {
281 fprintf(stderr, "%s: %p was never registered\n", __func__, handle);
282 abort();
283 }
284
285 Class **classes = &_classes;
286 while (*classes) {
287 Class *clazz = *classes;
288
289 if (clazz->image == image) {
290 *classes = clazz->next;
291 clazz->next = NULL;
292 } else {
293 classes = &clazz->next;
294 }
295 }
296
297 pthread_mutex_unlock(&_classesLock);
298}
299
300Class *classForName(const char *name) {
301
302 if (name) {
303 pthread_mutex_lock(&_classesLock);
304
305 Class *c = _classes;
306 while (c) {
307 if (strcmp(name, c->def.name) == 0) {
308 break;
309 }
310 c = c->next;
311 }
312
313 pthread_mutex_unlock(&_classesLock);
314
315 if (c) {
316 return c;
317 }
318
319 char *s;
320 if (asprintf(&s, "_%s", name) > 0) {
321 Class *clazz = NULL;
322 Class *(*archetype)(void) = NULL;
323
324 for (ClassImage *i = __atomic_load_n(&_images, __ATOMIC_ACQUIRE);
325 i && archetype == NULL; i = i->next) {
326
327 ident handle = __atomic_load_n(&i->handle, __ATOMIC_ACQUIRE);
328 if (handle) {
329 archetype = dlsym(handle, s);
330 }
331 }
332
333 if (archetype == NULL) {
334#if defined(_WIN32)
335 static Once once;
336 static ident handle;
337 do_once(&once, { handle = dlopen(NULL, RTLD_LAZY); });
338 archetype = handle ? dlsym(handle, s) : NULL;
339#else
340 archetype = dlsym(RTLD_DEFAULT, s);
341#endif
342 }
343
344 if (archetype) {
345 clazz = archetype();
346 }
347
348 free(s);
349 return clazz;
350 }
351 }
352
353 return NULL;
354}
355
357
358 if (obj) {
359 Object *object = cast(Object, obj);
360
361 assert(object);
362
363 if (__atomic_fetch_sub(&object->referenceCount, 1, __ATOMIC_RELEASE) == 1) {
364 __atomic_thread_fence(__ATOMIC_ACQUIRE);
365 $(object, dealloc);
366 }
367 }
368
369 return NULL;
370}
371
373
374 Object *object = cast(Object, obj);
375
376 assert(object);
377
378 /* A reference count of zero means another thread is already inside dealloc,
379 * and the caller is retaining memory that is about to be freed. */
380 unsigned int referenceCount = __atomic_load_n(&object->referenceCount, __ATOMIC_RELAXED);
381 do {
382 if (referenceCount == 0) {
383 fprintf(stderr, "%s: %p is being deallocated\n", __func__, object);
384 abort();
385 }
386 } while (!__atomic_compare_exchange_n(&object->referenceCount, &referenceCount,
387 referenceCount + 1, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
388
389 return obj;
390}
static void dealloc(Object *self)
Definition Array.c:99
void addClassImage(ident handle, const ident address)
Registers an image that provides Classes, e.g. a plugin.
Definition Class.c:231
static const ident imageForAddress(const ident address)
Definition Class.c:131
static void teardown(void)
Called atexit to teardown Objectively.
Definition Class.c:76
ident _alloc(Class *clazz)
Instantiate a type through the given Class.
Definition Class.c:198
static pthread_mutex_t _classesLock
Guards the structure of _classes. MUST NOT be held across dlsym, dlopen, or a Class initializer,...
Definition Class.c:52
static void setup(void)
Called when initializing Object to setup Objectively.
Definition Class.c:113
size_t _pageSize
Definition Class.c:44
static ClassImage * _images
The registered images providing Classes, most recently added first. Published atomically rather than ...
Definition Class.c:71
Class * classForName(const char *name)
Definition Class.c:300
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
static Class * _classes
Definition Class.c:46
void removeClassImage(ident handle)
Unregisters an image, and every Class it declared.
Definition Class.c:255
ident retain(ident obj)
Atomically increment the given Object's reference count.
Definition Class.c:372
ident _cast(const Class *clazz, const ident obj)
Perform a type-checking cast.
Definition Class.c:212
Classes describe the state and behavior of an Objectively type.
#define obj
#define cast(type, obj)
Safely cast obj to type.
Definition Class.h:232
#define OBJECTIVELY_MAGIC
The header value identifying Objectively types.
Definition Class.h:209
static void info(const Log *self, const char *fmt,...)
Definition Log.c:118
Class * _Object(void)
Definition Object.c:136
Object is the root Class of The Objectively Class hierarchy.
UTF-8 strings.
static Unicode next(StringReader *self, StringReaderMode mode)
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
ClassDefs are passed to _initialize via an archetype to initialize a Class.
Definition Class.h:41
size_t instanceSize
The instance size (required).
Definition Class.h:69
size_t interfaceSize
The interface size (required).
Definition Class.h:74
Class * superclass
The superclass (required). e.g. _Object().
Definition Class.h:84
void(* destroy)(Class *clazz)
The Class destructor (optional). This method is run for initialized Classes when your application exi...
Definition Class.h:47
const char * name
The Class name (required).
Definition Class.h:79
void(* initialize)(Class *clazz)
The Class initializer (optional).
Definition Class.h:64
The runtime representation of a Class.
Definition Class.h:90
ident image
The base address of the image that declared this Class.
Definition Class.h:113
ClassDef def
The Class definition.
Definition Class.h:95
Class * next
Provides chaining of initialized Classes.
Definition Class.h:105
ident interface
The interface of the Class.
Definition Class.h:100
A registered image: the handle the application holds, and the base address that the Classes it declar...
Definition Class.c:59
ident image
Definition Class.c:61
ident handle
Definition Class.c:60
ClassImage * next
Definition Class.c:62
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46
unsigned int magic
A header to allow introspection of Object types.
Definition Object.h:50