Skip to content

Commit 938216c

Browse files
committed
Fix #642, make OS_TaskDelete synchronous
In the POSIX implementation, OS_TaskDelete was implemented in a deferred manner - the API call was a request, and the actual deletion occured sometime thereafter. This is a problem if the task is running code within a dynamically loaded module, and the intent is to delete the task so the module can be unloaded. In this case the app needs to be certain that the task has actually been deleted before unloading can be done safely. To do this requires use of pthread_join() on POSIX which confirms that the task has exited. However, this is a (potentially) blocking call, so to do this requires rework of the EXCLUSIVE lock mode such that the OSAL lock is _not_ held during the join operation.
1 parent 9407cdf commit 938216c

12 files changed

Lines changed: 267 additions & 85 deletions

File tree

src/os/posix/src/os-impl-tasks.c

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,16 @@ int32 OS_Posix_InternalTaskCreate_Impl(pthread_t *pthr, osal_priority_t priority
477477
return (OS_ERROR);
478478
}
479479

480+
/*
481+
** Set the thread to be joinable by default
482+
*/
483+
return_code = pthread_attr_setdetachstate(&custom_attr, PTHREAD_CREATE_JOINABLE);
484+
if (return_code != 0)
485+
{
486+
OS_DEBUG("pthread_attr_setdetachstate error in OS_TaskCreate: %s\n", strerror(return_code));
487+
return (OS_ERROR);
488+
}
489+
480490
/*
481491
** Test to see if the original main task scheduling priority worked.
482492
** If so, then also set the attributes for this task. Otherwise attributes
@@ -541,12 +551,6 @@ int32 OS_Posix_InternalTaskCreate_Impl(pthread_t *pthr, osal_priority_t priority
541551
** Do not treat anything bad that happens after this point as fatal.
542552
** The task is running, after all - better to leave well enough alone.
543553
*/
544-
return_code = pthread_detach(*pthr);
545-
if (return_code != 0)
546-
{
547-
OS_DEBUG("pthread_detach error in OS_TaskCreate: %s\n", strerror(return_code));
548-
}
549-
550554
return_code = pthread_attr_destroy(&custom_attr);
551555
if (return_code != 0)
552556
{
@@ -583,6 +587,33 @@ int32 OS_TaskCreate_Impl(const OS_object_token_t *token, uint32 flags)
583587
return return_code;
584588
} /* end OS_TaskCreate_Impl */
585589

590+
/*----------------------------------------------------------------
591+
*
592+
* Function: OS_TaskDetach_Impl
593+
*
594+
* Purpose: Implemented per internal OSAL API
595+
* See prototype for argument/return detail
596+
*
597+
*-----------------------------------------------------------------*/
598+
int32 OS_TaskDetach_Impl(const OS_object_token_t *token)
599+
{
600+
OS_impl_task_internal_record_t *impl;
601+
int ret;
602+
603+
impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token);
604+
605+
ret = pthread_detach(impl->id);
606+
607+
if (ret != 0)
608+
{
609+
OS_DEBUG("pthread_detach: Failed on Task ID = %lu, err = %s\n",
610+
OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), strerror(ret));
611+
return OS_ERROR;
612+
}
613+
614+
return OS_SUCCESS;
615+
}
616+
586617
/*----------------------------------------------------------------
587618
*
588619
* Function: OS_TaskMatch_Impl
@@ -616,6 +647,8 @@ int32 OS_TaskMatch_Impl(const OS_object_token_t *token)
616647
int32 OS_TaskDelete_Impl(const OS_object_token_t *token)
617648
{
618649
OS_impl_task_internal_record_t *impl;
650+
void *retval;
651+
int ret;
619652

620653
impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token);
621654

@@ -625,7 +658,35 @@ int32 OS_TaskDelete_Impl(const OS_object_token_t *token)
625658
** to cancel here is that the thread ID is invalid because it already exited itself,
626659
** and if that is true there is nothing wrong - everything is OK to continue normally.
627660
*/
628-
pthread_cancel(impl->id);
661+
ret = pthread_cancel(impl->id);
662+
if (ret != 0)
663+
{
664+
OS_DEBUG("pthread_cancel: Failed on Task ID = %lu, err = %s\n",
665+
OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), strerror(ret));
666+
667+
/* fall through (will still return OS_SUCCESS) */
668+
}
669+
else
670+
{
671+
/*
672+
* Note that "pthread_cancel" is a request - and successful return above
673+
* only means that the cancellation request is pending.
674+
*
675+
* pthread_join() will wait until the thread has actually exited.
676+
*
677+
* This is important for CFE, as task deletion often occurs in
678+
* conjunction with an application reload - which means the next
679+
* call is likely to be OS_ModuleUnload(). So is critical that all
680+
* tasks potentially executing code within that module have actually
681+
* been stopped - not just pending cancellation.
682+
*/
683+
ret = pthread_join(impl->id, &retval);
684+
if (ret != 0)
685+
{
686+
OS_DEBUG("pthread_join: Failed on Task ID = %lu, err = %s\n",
687+
OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), strerror(ret));
688+
}
689+
}
629690
return OS_SUCCESS;
630691

631692
} /* end OS_TaskDelete_Impl */
@@ -731,6 +792,17 @@ int32 OS_TaskRegister_Impl(osal_id_t global_task_id)
731792
{
732793
int32 return_code;
733794
OS_U32ValueWrapper_t arg;
795+
int old_state;
796+
int old_type;
797+
798+
/*
799+
* Set cancel state=ENABLED, type=DEFERRED
800+
* This should be the default for new threads, but
801+
* setting explicitly to be sure that a pthread_join()
802+
* will work as expected in case this thread is deleted.
803+
*/
804+
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state);
805+
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_type);
734806

735807
arg.opaque_arg = 0;
736808
arg.id = global_task_id;

src/os/posix/src/os-impl-timebase.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ int32 OS_TimeBaseCreate_Impl(const OS_object_token_t *token)
386386
*/
387387
for (idx = 0; idx < OS_MAX_TIMEBASES; ++idx)
388388
{
389-
if (OS_ObjectIdDefined(OS_global_timebase_table[idx].active_id) &&
389+
if (OS_ObjectIdIsValid(OS_global_timebase_table[idx].active_id) &&
390390
OS_impl_timebase_table[idx].assigned_signal != 0)
391391
{
392392
sigaddset(&local->sigset, OS_impl_timebase_table[idx].assigned_signal);

src/os/rtems/src/os-impl-tasks.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ int32 OS_TaskDelete_Impl(const OS_object_token_t *token)
171171
return OS_SUCCESS;
172172
} /* end OS_TaskDelete_Impl */
173173

174+
/*----------------------------------------------------------------
175+
*
176+
* Function: OS_TaskDetach_Impl
177+
*
178+
* Purpose: Implemented per internal OSAL API
179+
* See prototype for argument/return detail
180+
*
181+
*-----------------------------------------------------------------*/
182+
int32 OS_TaskDetach_Impl(const OS_object_token_t *token)
183+
{
184+
/* No-op on RTEMS */
185+
return OS_SUCCESS;
186+
}
187+
174188
/*----------------------------------------------------------------
175189
*
176190
* Function: OS_TaskExit_Impl

src/os/shared/inc/os-shared-idmap.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030

3131
#include <os-shared-globaldefs.h>
3232

33-
#define OS_OBJECT_EXCL_REQ_FLAG 0x0001
34-
3533
#define OS_OBJECT_ID_RESERVED ((osal_id_t) {0xFFFFFFFF})
3634

3735
/*
@@ -43,7 +41,6 @@ struct OS_common_record
4341
osal_id_t active_id;
4442
osal_id_t creator;
4543
uint16 refcount;
46-
uint16 flags;
4744
};
4845

4946
/*
@@ -214,6 +211,26 @@ static inline void OS_ObjectIdCompose_Impl(osal_objtype_t idtype, uint32 idseria
214211
*result = OS_ObjectIdFromInteger((idtype << OS_OBJECT_TYPE_SHIFT) | idserial);
215212
}
216213

214+
/*-------------------------------------------------------------------------------------*/
215+
/**
216+
* @brief Check if an object ID represents a valid/active value.
217+
*
218+
* This tests that the ID value is within the range specifically used by
219+
* valid OSAL IDs. This is smaller than the set of defined IDs.
220+
*
221+
* For example, the value of OS_OBJECT_ID_RESERVED is defined but not valid.
222+
* So while OS_ObjectIdDefined() will match entries being actively created or
223+
* deleted, OS_ObjectIdIsValid() will not.
224+
*
225+
* @param[in] object_id The object ID
226+
* @returns true if table entry is valid
227+
*/
228+
static inline bool OS_ObjectIdIsValid(osal_id_t object_id)
229+
{
230+
osal_objtype_t objtype = OS_ObjectIdToType_Impl(object_id);
231+
return (objtype > OS_OBJECT_TYPE_UNDEFINED && objtype < OS_OBJECT_TYPE_USER);
232+
}
233+
217234
/*----------------------------------------------------------------
218235
Function: OS_GetMaxForObjectType
219236

src/os/shared/inc/os-shared-task.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ int32 OS_TaskMatch_Impl(const OS_object_token_t *token);
9494
------------------------------------------------------------------*/
9595
int32 OS_TaskCreate_Impl(const OS_object_token_t *token, uint32 flags);
9696

97+
/*----------------------------------------------------------------
98+
Function: OS_TaskDetach_Impl
99+
100+
Purpose: Sets the thread so that the OS resources associated with the task
101+
will be released when the thread exits itself
102+
103+
Returns: OS_SUCCESS on success, or relevant error code
104+
------------------------------------------------------------------*/
105+
int32 OS_TaskDetach_Impl(const OS_object_token_t *token);
106+
97107
/*----------------------------------------------------------------
98108
Function: OS_TaskDelete_Impl
99109

0 commit comments

Comments
 (0)