/*
* Copyright (c) 2008-2023 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#include "config.h"
#import "OFTLSKey.h"
#define Class IntuitionClass
#include <exec/semaphores.h>
#include <proto/exec.h>
#undef Class
/*
* As we use this file in both the runtime and ObjFW, and since AmigaOS always
* has the runtime, use the hashtable from the runtime.
*/
#import "runtime/private.h"
static OFTLSKey firstKey = NULL, lastKey = NULL;
static struct SignalSemaphore semaphore;
static bool semaphoreInitialized = false;
static uint32_t
hashFunc(const void *ptr)
{
return (uint32_t)(uintptr_t)ptr;
}
static bool
equalFunc(const void *ptr1, const void *ptr2)
{
return (ptr1 == ptr2);
}
OF_CONSTRUCTOR()
{
if (!semaphoreInitialized) {
InitSemaphore(&semaphore);
semaphoreInitialized = true;
}
}
int
OFTLSKeyNew(OFTLSKey *key)
{
if (!semaphoreInitialized) {
/*
* We might be called from another constructor, while ours has
* not run yet. This is safe, as the constructor is definitely
* run before a thread is spawned.
*/
InitSemaphore(&semaphore);
semaphoreInitialized = true;
}
if ((*key = malloc(sizeof(**key))) == NULL)
return ENOMEM;
(*key)->table = NULL;
ObtainSemaphore(&semaphore);
@try {
(*key)->next = NULL;
(*key)->previous = lastKey;
if (lastKey != NULL)
lastKey->next = *key;
lastKey = *key;
if (firstKey == NULL)
firstKey = *key;
} @finally {
ReleaseSemaphore(&semaphore);
}
/* We create the hash table lazily. */
return 0;
}
int
OFTLSKeyFree(OFTLSKey key)
{
ObtainSemaphore(&semaphore);
@try {
if (key->previous != NULL)
key->previous->next = key->next;
if (key->next != NULL)
key->next->previous = key->previous;
if (firstKey == key)
firstKey = key->next;
if (lastKey == key)
lastKey = key->previous;
objc_hashtable_free(key->table);
free(key);
} @finally {
ReleaseSemaphore(&semaphore);
}
return 0;
}
void *
OFTLSKeyGet(OFTLSKey key)
{
void *ret;
ObtainSemaphore(&semaphore);
@try {
if (key->table == NULL)
return NULL;
ret = objc_hashtable_get(key->table, FindTask(NULL));
} @finally {
ReleaseSemaphore(&semaphore);
}
return ret;
}
int
OFTLSKeySet(OFTLSKey key, void *ptr)
{
ObtainSemaphore(&semaphore);
@try {
struct Task *task = FindTask(NULL);
if (key->table == NULL)
key->table = objc_hashtable_new(hashFunc, equalFunc, 2);
if (ptr == NULL)
objc_hashtable_delete(key->table, task);
else
objc_hashtable_set(key->table, task, ptr);
} @finally {
ReleaseSemaphore(&semaphore);
}
return 0;
}
void
OFTLSKeyThreadExited(void)
{
ObtainSemaphore(&semaphore);
@try {
struct Task *task = FindTask(NULL);
for (OFTLSKey iter = firstKey; iter != NULL; iter = iter->next)
if (iter->table != NULL)
objc_hashtable_delete(iter->table, task);
} @finally {
ReleaseSemaphore(&semaphore);
}
}