/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <errno.h>
#import "OFPlainThread.h"
#import "OFData.h"
#import "OFString.h"
#import "OFTLSKey.h"
#define Class IntuitionClass
#include <dos/dostags.h>
#include <proto/dos.h>
#include <proto/exec.h>
#undef Class
#ifndef OF_MORPHOS
extern void OFTLSKeyThreadExited(void);
#endif
static OFTLSKey threadKey;
OF_CONSTRUCTOR()
{
OFEnsure(OFTLSKeyNew(&threadKey) == 0);
}
static void
functionWrapper(void)
{
bool detached = false;
OFPlainThread thread =
(OFPlainThread)((struct Process *)FindTask(NULL))->pr_ExitData;
OFEnsure(OFTLSKeySet(threadKey, thread) == 0);
thread->function(thread->object);
ObtainSemaphore(&thread->semaphore);
@try {
thread->done = true;
#ifndef OF_MORPHOS
OFTLSKeyThreadExited();
#endif
if (thread->detached)
detached = true;
else if (thread->joinTask != NULL)
Signal(thread->joinTask, (1ul << thread->joinSigBit));
} @finally {
ReleaseSemaphore(&thread->semaphore);
}
if (detached)
free(thread);
}
int
OFPlainThreadAttributesInit(OFPlainThreadAttributes *attr)
{
attr->priority = 0;
attr->stackSize = 0;
return 0;
}
int
OFPlainThreadNew(OFPlainThread *thread, const char *name, void (*function)(id),
id object, const OFPlainThreadAttributes *attr)
{
OFMutableData *tags = nil;
if ((*thread = calloc(1, sizeof(**thread))) == NULL)
return ENOMEM;
@try {
(*thread)->function = function;
(*thread)->object = object;
InitSemaphore(&(*thread)->semaphore);
tags = [[OFMutableData alloc]
initWithItemSize: sizeof(struct TagItem)
capacity: 12];
#define ADD_TAG(tag, data) \
{ \
struct TagItem t = { \
.ti_Tag = tag, \
.ti_Data = data \
}; \
[tags addItem: &t]; \
}
ADD_TAG(NP_Entry, (ULONG)functionWrapper)
ADD_TAG(NP_ExitData, (ULONG)*thread)
#ifdef OF_AMIGAOS4
ADD_TAG(NP_Child, TRUE)
#endif
#ifdef OF_MORPHOS
ADD_TAG(NP_CodeType, CODETYPE_PPC);
#endif
if (name != NULL)
ADD_TAG(NP_Name, (ULONG)name);
ADD_TAG(NP_Input, ((struct Process *)FindTask(NULL))->pr_CIS)
ADD_TAG(NP_Output, ((struct Process *)FindTask(NULL))->pr_COS)
ADD_TAG(NP_Error, ((struct Process *)FindTask(NULL))->pr_CES)
ADD_TAG(NP_CloseInput, FALSE)
ADD_TAG(NP_CloseOutput, FALSE)
ADD_TAG(NP_CloseError, FALSE)
if (attr != NULL && attr->priority != 0) {
if (attr->priority < 1 || attr->priority > 1)
return EINVAL;
/*
* -1 should be -128 (lowest possible priority) while
* +1 should be +127 (highest possible priority).
*/
ADD_TAG(NP_Priority, (attr->priority > 0
? attr->priority * 127 : attr->priority * 128))
}
if (attr != NULL && attr->stackSize != 0)
ADD_TAG(NP_StackSize, attr->stackSize)
else
ADD_TAG(NP_StackSize,
((struct Process *)FindTask(NULL))->pr_StackSize)
ADD_TAG(TAG_DONE, 0)
#undef ADD_TAG
(*thread)->task = (struct Task *)CreateNewProc(tags.items);
if ((*thread)->task == NULL) {
free(*thread);
return EAGAIN;
}
} @catch (id e) {
free(*thread);
@throw e;
} @finally {
[tags release];
}
return 0;
}
OFPlainThread
OFCurrentPlainThread(void)
{
return OFTLSKeyGet(threadKey);
}
bool
OFPlainThreadIsCurrent(OFPlainThread thread)
{
return (thread->task == FindTask(NULL));
}
int
OFPlainThreadJoin(OFPlainThread thread)
{
ObtainSemaphore(&thread->semaphore);
if (thread->done) {
ReleaseSemaphore(&thread->semaphore);
free(thread);
return 0;
}
@try {
if (thread->detached || thread->joinTask != NULL)
return EINVAL;
if ((thread->joinSigBit = AllocSignal(-1)) == -1)
return EAGAIN;
thread->joinTask = FindTask(NULL);
} @finally {
ReleaseSemaphore(&thread->semaphore);
}
Wait(1ul << thread->joinSigBit);
FreeSignal(thread->joinSigBit);
OFAssert(thread->done);
free(thread);
return 0;
}
int
OFPlainThreadDetach(OFPlainThread thread)
{
ObtainSemaphore(&thread->semaphore);
if (thread->done)
free(thread);
else
thread->detached = true;
ReleaseSemaphore(&thread->semaphore);
return 0;
}
void
OFSetThreadName(const char *name)
{
}