feat(pthread): add basic support for pthread_detach and pthread_cancel

Signed-off-by: Caoruihong <crh.cao@huawei.com>
Change-Id: I280794d731f064bb5283cf7466971f0b90d40534
This commit is contained in:
Caoruihong
2021-11-12 02:02:31 +08:00
parent 307b650dcf
commit f51cd06b34
+241 -65
View File
@@ -36,36 +36,85 @@
#include "los_task.h"
#include "los_task_pri.h"
#define POLLING_INTERVAL_FOR_JOIN 1000
#define PTHREAD_NAMELEN 16
/* this is just an assertion: LOS_TASK_ARG_NUM >= 4 */
typedef char NULNAM[-!((LOS_TASK_ARG_NUM * 4) >= PTHREAD_NAMELEN)];
struct PthreadData {
CHAR name[PTHREAD_NAMELEN];
BOOL detached;
BOOL exited;
VOID *exitCode;
};
/**
* pthread id and native task id translation is use for preventing
* wrongly passing pthread id to LOS_TaskXXX API as a native task id.
*/
/* translate pthread_t id to taskID */
static inline UINT32 P2T(pthread_t id)
{
return (UINT32)id - g_taskMaxNum;
}
/* translate taskID to pthread_t id */
static inline pthread_t T2P(UINT32 id)
{
return (pthread_t)(id + g_taskMaxNum);
}
/**
* Only pthreads can be set name by pthread_setname_np.
* Native threads are detached by default and its name can't be set.
*/
static inline BOOL IsValidTask(UINT32 taskID)
{
return (taskID < g_taskMaxNum) &&
!(OS_TCB_FROM_TID(taskID)->taskStatus & OS_TASK_STATUS_UNUSED);
}
static void *PthreadEntry(UINT32 param1, UINT32 param2, UINT32 param3, UINT32 param4);
static inline BOOL IsPthread(UINT32 taskID)
{
return IsValidTask(taskID) &&
(OS_TCB_FROM_TID(taskID)->taskEntry == PthreadEntry);
}
static inline struct PthreadData *GetPthreadData(UINT32 taskID)
{
return IsPthread(taskID) ?
(struct PthreadData *)(UINTPTR)(OS_TCB_FROM_TID(taskID)->taskName) : NULL;
}
static void *PthreadEntry(UINT32 param1, UINT32 param2, UINT32 param3, UINT32 param4)
{
void *(*startRoutine)(void *) = (void *)(UINTPTR)param1;
void *param = (void *)(UINTPTR)param2;
void *retVal = NULL;
UINT32 taskID = LOS_CurTaskIDGet();
struct PthreadData *pthreadData = NULL;
(void)param3;
(void)param4;
int ret;
LosTaskCB *tcb = OS_TCB_FROM_TID(LOS_CurTaskIDGet());
char *tmp = tcb->taskName;
tcb->taskName = (char *)tcb->args; /* args are reused as task name */
ret = strcpy_s(tcb->taskName, PTHREAD_NAMELEN, tmp);
if (ret != 0) {
free(tmp);
return NULL;
retVal = startRoutine(param);
pthreadData = GetPthreadData(taskID);
if (pthreadData->detached) {
free(pthreadData);
pthreadData = NULL;
} else {
pthreadData->exitCode = retVal;
pthreadData->exited = TRUE;
if (LOS_ERRNO_TSK_SUSPEND_LOCKED == LOS_TaskSuspend(taskID)) {
LOS_TaskUnlock();
}
while (LOS_ERRNO_TSK_SUSPEND_LOCKED != LOS_TaskSuspend(taskID)) {
}
}
free(tmp);
return startRoutine(param);
}
static inline int IsPthread(pthread_t thread)
{
return ((UINT32)thread <= LOSCFG_BASE_CORE_TSK_LIMIT) &&
(OS_TCB_FROM_TID((UINT32)thread)->taskEntry == PthreadEntry);
return retVal;
}
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
@@ -73,18 +122,27 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
{
TSK_INIT_PARAM_S taskInitParam = {0};
UINT32 taskID;
struct PthreadData *pthreadData = NULL;
if ((thread == NULL) || (startRoutine == NULL)) {
return EINVAL;
}
pthreadData = malloc(sizeof(struct PthreadData));
if (pthreadData == NULL) {
return ENOMEM;
}
(void)memset_s(pthreadData, sizeof(struct PthreadData), 0, sizeof(struct PthreadData));
taskInitParam.usTaskPrio = LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO;
taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
if (attr) {
if (attr->detachstate == PTHREAD_CREATE_DETACHED) {
return ENOTSUP;
pthreadData->detached = TRUE;
}
if (attr->stackaddr_set) {
free(pthreadData);
return ENOTSUP;
}
if (attr->stacksize_set) {
@@ -93,57 +151,68 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
taskInitParam.usTaskPrio = (UINT16)attr->schedparam.sched_priority;
}
taskInitParam.pcName = malloc(PTHREAD_NAMELEN);
if (taskInitParam.pcName == NULL) {
return ENOMEM;
}
taskInitParam.pcName = pthreadData->name;
taskInitParam.pfnTaskEntry = PthreadEntry;
taskInitParam.auwArgs[0] = (UINT32)(UINTPTR)startRoutine;
taskInitParam.auwArgs[1] = (UINT32)(UINTPTR)arg;
if (LOS_TaskCreateOnly(&taskID, &taskInitParam) != LOS_OK) {
free(taskInitParam.pcName);
return EINVAL;
free(pthreadData);
return EAGAIN;
}
/* set pthread default name */
(void)sprintf_s(taskInitParam.pcName, PTHREAD_NAMELEN, "pthread%u", taskID);
(void)sprintf_s(taskInitParam.pcName, PTHREAD_NAMELEN, "pthread%u", (UINT32)T2P(taskID));
(void)LOS_TaskResume(taskID);
*thread = (pthread_t)taskID;
*thread = T2P(taskID);
return 0;
}
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
{
if ((param == NULL) || (param->sched_priority < OS_TASK_PRIORITY_HIGHEST) ||
(param->sched_priority >= OS_TASK_PRIORITY_LOWEST) || !IsPthread(thread)) {
UINT32 taskID = P2T(thread);
if (param == NULL) {
return EINVAL;
}
if (!IsValidTask(taskID)) {
return ESRCH;
}
/* Only support SCHED_RR policy now */
if (policy != SCHED_RR) {
return ENOTSUP;
}
if (LOS_TaskPriSet((UINT32)thread, (UINT16)param->sched_priority) != LOS_OK) {
if ((param->sched_priority < OS_TASK_PRIORITY_HIGHEST) ||
(param->sched_priority >= OS_TASK_PRIORITY_LOWEST)) {
return EINVAL;
}
if (LOS_TaskPriSet(taskID, (UINT16)param->sched_priority) != LOS_OK) {
return EPERM;
}
return 0;
}
int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
{
UINT32 prio;
UINT32 taskID = P2T(thread);
if ((policy == NULL) || (param == NULL) || !IsPthread(thread)) {
if ((policy == NULL) || (param == NULL)) {
return EINVAL;
}
prio = LOS_TaskPriGet((UINT32)thread);
if (!IsValidTask(taskID)) {
return ESRCH;
}
prio = LOS_TaskPriGet(taskID);
if (prio == OS_INVALID) {
return EINVAL;
}
@@ -155,16 +224,53 @@ int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *par
pthread_t pthread_self(void)
{
return (pthread_t)LOS_CurTaskIDGet();
UINT32 taskID = LOS_CurTaskIDGet();
return T2P(taskID);
}
int pthread_cancel(pthread_t thread)
{
if (!IsPthread(thread)) {
UINT32 taskID = P2T(thread);
struct PthreadData *pthreadData = NULL;
if (!IsValidTask(taskID)) {
return ESRCH;
}
if (thread == pthread_self()) {
return EDEADLK;
}
if (!IsPthread(taskID)) {
switch (LOS_TaskDelete(taskID)) {
case LOS_OK:
return 0;
case LOS_ERRNO_TSK_NOT_CREATED:
return ESRCH;
case LOS_ERRNO_TSK_OPERATE_IDLE:
case LOS_ERRNO_TSK_SUSPEND_SWTMR_NOT_ALLOWED:
return EPERM;
default:
break;
}
return EINVAL;
}
return ENOSYS;
pthreadData = GetPthreadData(taskID);
if (pthreadData->detached) {
(void)LOS_TaskDelete(taskID);
free(pthreadData);
return 0;
}
if (pthreadData->exited) {
return 0;
}
pthreadData->exitCode = PTHREAD_CANCELED;
pthreadData->exited = TRUE;
LOS_TaskSuspend(taskID);
return 0;
}
static void *VoidTask(UINT32 param1, UINT32 param2, UINT32 param3, UINT32 param4)
@@ -192,53 +298,116 @@ static void CleanupTaskResource(void)
int pthread_join(pthread_t thread, void **retval)
{
UINT32 taskStatus;
if (!IsPthread(thread)) {
return EINVAL;
struct PthreadData *pthreadData = NULL;
UINT32 taskID = P2T(thread);
if (!IsValidTask(taskID)) {
return ESRCH;
}
if (retval) {
/* retrieve thread exit code is not supported currently */
return ENOTSUP;
if (!IsPthread(taskID)) {
/* native thread is always detached so it can't be join */
return EINVAL;
}
if (thread == pthread_self()) {
return EDEADLK;
}
while (LOS_TaskStatusGet((UINT32)thread, &taskStatus) == LOS_OK) {
usleep(10000);
pthreadData = GetPthreadData(taskID);
while (LOS_TaskStatusGet(taskID, &taskStatus) == LOS_OK) {
if (pthreadData->detached) {
/* detached thread can't be join */
return EINVAL;
}
if ((taskStatus & OS_TASK_STATUS_SUSPEND) && pthreadData->exited) {
if (retval) {
*retval = pthreadData->exitCode;
}
(void)LOS_TaskDelete(taskID);
free(pthreadData);
pthreadData = NULL;
CleanupTaskResource();
return 0;
}
/* we use usleep to simplify this implementation or we need an extra semaphore for each pthread */
usleep(POLLING_INTERVAL_FOR_JOIN);
}
CleanupTaskResource();
return 0;
return ESRCH;
}
int pthread_detach(pthread_t thread)
{
if (!IsPthread(thread)) {
UINT32 taskID = P2T(thread);
struct PthreadData *pthreadData = NULL;
if (!IsValidTask(taskID)) {
return ESRCH;
}
if (!IsPthread(taskID)) {
/* native thread is always detached so it can't be detach again */
return EINVAL;
}
return ENOSYS;
pthreadData = GetPthreadData(taskID);
if (pthreadData->detached) {
/* detached thread can't be detach again */
return EINVAL;
}
if (pthreadData->exited) {
(void)LOS_TaskDelete(taskID);
free(pthreadData);
return 0;
}
pthreadData->detached = TRUE;
return 0;
}
void pthread_exit(void *retVal)
{
(void)retVal;
(void)LOS_TaskDelete(LOS_CurTaskIDGet());
UINT32 taskID = LOS_CurTaskIDGet();
struct PthreadData *pthreadData = NULL;
if (!IsPthread(taskID)) {
/* native thread just delete self */
while (LOS_OK != LOS_TaskDelete(taskID)) {
}
}
pthreadData = GetPthreadData(taskID);
if (pthreadData->detached) {
free(pthreadData);
while (LOS_OK != LOS_TaskDelete(taskID)) {
}
} else {
pthreadData->exitCode = retVal;
pthreadData->exited = TRUE;
while (LOS_ERRNO_TSK_SUSPEND_LOCKED != LOS_TaskSuspend(taskID)) {
}
}
}
int pthread_setname_np(pthread_t thread, const char *name)
{
char *taskName = LOS_TaskNameGet((UINT32)thread);
if (taskName == NULL || !IsPthread(thread)) {
return EINVAL;
UINT32 taskID = P2T(thread);
if (!IsValidTask(taskID)) {
return ESRCH;
}
if (strnlen(name, PTHREAD_NAMELEN) >= PTHREAD_NAMELEN) {
if (!IsPthread(taskID)) {
/* native thread's name is readonly so it can not be set */
return EPERM;
}
/* ensure the following strcpy_s not fail and making unneeded side effect (set src to empty string) */
if (strlen(name) >= PTHREAD_NAMELEN) {
return ERANGE;
}
if (strcpy_s(taskName, PTHREAD_NAMELEN, name) != 0) {
if (strcpy_s(LOS_TaskNameGet(taskID), PTHREAD_NAMELEN, name) != EOK) {
return ERANGE;
}
return 0;
@@ -246,17 +415,24 @@ int pthread_setname_np(pthread_t thread, const char *name)
int pthread_getname_np(pthread_t thread, char *buf, size_t buflen)
{
int ret;
const char *name = NULL;
UINT32 taskID = P2T(thread);
if (!IsValidTask(taskID)) {
return ESRCH;
}
const char *name = LOS_TaskNameGet((UINT32)thread);
if (name == NULL || !IsPthread(thread)) {
return EINVAL;
name = LOS_TaskNameGet(taskID);
if (name == NULL) {
name = "";
}
if (buflen > strlen(name)) {
ret = strcpy_s(buf, buflen, name);
if (ret == 0) {
return 0;
}
/* ensure the following strcpy_s not fail and making unneeded side effect (set src to empty string) */
if (strlen(name) >= PTHREAD_NAMELEN) {
return ERANGE;
}
return ERANGE;
if (strcpy_s(buf, buflen, name) != EOK) {
return ERANGE;
}
return 0;
}