📄 task.c
字号:
/*****************************************************************************
* task.c - defines the wrapper functions and data structures needed
* to implement a Wind River pSOS+ (R) task control API
* in a POSIX Threads environment.
****************************************************************************/
#include <errno.h>
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include "p2pthread.h"
#undef DIAG_PRINTFS
#define T_NOPREEMPT 0x01
#define T_TSLICE 0x02
#define ERR_TIMEOUT 0x01
#define ERR_NODENO 0x04
#define ERR_OBJDEL 0x05
#define ERR_OBJTFULL 0x08
#define ERR_OBJNF 0x09
#define ERR_PRIOR 0x11
#define ERR_ACTIVE 0x12
#define ERR_SUSP 0x14
#define ERR_NOTSUSP 0x15
#define ERR_REGNUM 0x17
/*
** user_sysroot is a user-defined function. It contains all initialization
** calls to create any tasks and other objects reqired for
** startup of the user's RTOS system environment. It is called
** from (and runs in) the system initialization pthread context.
** It may optionally wait for some condition, shut down the
** user's RTOS system environment, clean up the resources used
** by the various RTOS objects, and return to the initialization
** pthread. The system initialization pthread will then
** terminate, as will the parent process.
*/
// extern void user_sysroot( void );
/*****************************************************************************
** p2pthread Global Data Structures
*****************************************************************************/
/*
** task_list is a linked list of pthread task control blocks.
** It is used to perform en-masse operations on all p2pthread
** tasks at once.
*/
static p2pthread_cb_t *
task_list = (p2pthread_cb_t *)NULL;
/*
** task_list_lock is a mutex used to serialize access to the task list
*/
static pthread_mutex_t
task_list_lock = PTHREAD_MUTEX_INITIALIZER;
/*
** p2pt_sched_lock is a mutex used to make sched_lock exclusive to one thread
** at a time.
*/
pthread_mutex_t
p2pt_sched_lock = PTHREAD_MUTEX_INITIALIZER;
/*
** scheduler_locked contains the pthread ID of the thread which currently
** has the scheduler locked (or NULL if it is unlocked).
*/
static pthread_t
scheduler_locked = (pthread_t)NULL;
/*
** sched_lock_level tracks recursive nesting levels of sched_lock/unlock calls
** so the scheduler is only unlocked at the outermost
** sched_unlock call.
*/
static unsigned long
sched_lock_level = 0;
/*
** sched_lock_change is a condition variable which signals a change from
** locked to unlocked or vice-versa.
*/
static pthread_cond_t
sched_lock_change = PTHREAD_COND_INITIALIZER;
/*****************************************************************************
** thread-safe malloc
*****************************************************************************/
void *ts_malloc( size_t blksize )
{
void *blkaddr;
static pthread_mutex_t
malloc_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&malloc_lock );
pthread_mutex_lock( &malloc_lock );
blkaddr = malloc( blksize );
pthread_mutex_unlock( &malloc_lock );
pthread_cleanup_pop( 0 );
return( blkaddr );
}
/*****************************************************************************
** thread-safe free
*****************************************************************************/
void ts_free( void *blkaddr )
{
static pthread_mutex_t
free_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&free_lock );
pthread_mutex_lock( &free_lock );
free( blkaddr );
pthread_mutex_unlock( &free_lock );
pthread_cleanup_pop( 0 );
}
/*****************************************************************************
** my_tcb - returns a pointer to the task control block for the calling task
*****************************************************************************/
p2pthread_cb_t *
my_tcb( void )
{
pthread_t my_pthrid;
p2pthread_cb_t *current_tcb;
/*
** Get caller's pthread ID
*/
my_pthrid = pthread_self();
/*
** If the task_list contains tasks, scan it for the tcb
** whose thread id matches the one to be deleted. No locking
** of the task list is done here since the access is read-only.
** NOTE that a tcb being appended to the task_list MUST have its
** nxt_task member initialized to NULL before being linked into
** the list.
*/
if ( task_list != (p2pthread_cb_t *)NULL )
{
for ( current_tcb = task_list;
current_tcb != (p2pthread_cb_t *)NULL;
current_tcb = current_tcb->nxt_task )
{
if ( my_pthrid == current_tcb->pthrid )
{
/*
** Found the task control_block.
*/
return( current_tcb );
}
}
}
/*
** No matching task found... return NULL
*/
return( (p2pthread_cb_t *)NULL );
}
/*****************************************************************************
** tcb_for - returns the address of the task control block for the task
** idenified by taskid
*****************************************************************************/
p2pthread_cb_t *
tcb_for( ULONG taskid )
{
p2pthread_cb_t *current_tcb;
int found_taskid;
if ( task_list != (p2pthread_cb_t *)NULL )
{
/*
** One or more tasks already exist in the task list...
** Scan the existing tasks for a matching ID.
*/
found_taskid = FALSE;
for ( current_tcb = task_list;
current_tcb != (p2pthread_cb_t *)NULL;
current_tcb = current_tcb->nxt_task )
{
if ( current_tcb->taskid == taskid )
{
found_taskid = TRUE;
break;
}
}
if ( found_taskid == FALSE )
/*
** No matching ID found
*/
current_tcb = (p2pthread_cb_t *)NULL;
}
else
current_tcb = (p2pthread_cb_t *)NULL;
return( current_tcb );
}
/*****************************************************************************
** sched_lock - 'locks the scheduler' to prevent preemption of the current task
** by other task-level code. Because we cannot actually lock the
** scheduler in a pthreads environment, we temporarily set the
** dynamic priority of the calling thread above that of any other
** thread, thus guaranteeing that no other tasks preempt it.
*****************************************************************************/
void
sched_lock( void )
{
pthread_t my_pthrid;
p2pthread_cb_t *tcb;
int max_priority, sched_policy, got_lock;
/*
** p2pt_sched_lock ensures that only one p2pthread pthread at a time gets
** to run at max_priority (effectively locking out all other p2pthread
** pthreads). Due to the semantics of the pthread_cleanup push/pop
** pairs (which protect against deadlocks in the event a thread gets
** terminated while holding the mutex lock), we cannot safely leave
** the mutex itself locked until sched_unlock() is called. Therefore,
** we instead use the mutex to provide 'atomic access' to a global
** flag indicating if the scheduler is currently locked. We will
** 'spin' and briefly suspend until the scheduler is unlocked, and
** will then lock it ourselves before proceeding.
*/
got_lock = FALSE;
my_pthrid = pthread_self();
/*
** 'Spin' here until scheduler_locked == NULL or our pthread ID
** This effectively prevents more than one pthread at a time from
** setting its priority to max_priority.
*/
do {
/*
** The pthread_cleanup_push/pop pair ensure the mutex will be
** unlocked if the calling thread gets killed within this loop.
*/
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&p2pt_sched_lock );
/*
** The mutex lock/unlock guarantees 'atomic' access to the
** scheduler_locked flag. Locking via pthread ID allows recursive
** locking by the same pthread while excluding all other pthreads.
*/
pthread_mutex_lock( &p2pt_sched_lock );
if ( (scheduler_locked == (pthread_t)NULL) ||
(scheduler_locked == my_pthrid) )
{
scheduler_locked = my_pthrid;
sched_lock_level++;
/*
** Note: since the type of 'sched_lock_level is defined as
** 'unsigned', so the condition below is only occur when
** sched_lock_level exceed it's maxium value. It's a bug
** although it is right in most of time.
*/
if ( sched_lock_level == 0L )
sched_lock_level--;
got_lock = TRUE;
/*
** Note: i think maybe the statement below is useless.
*/
pthread_cond_broadcast( &sched_lock_change );
#ifdef DIAG_PRINTFS
printf( "\r\nsched_lock sched_lock_level %lu locking tid %ld",
sched_lock_level, scheduler_locked );
#endif
}
else
{
#ifdef DIAG_PRINTFS
printf( "\r\nsched_lock locking tid %ld my tid %ld",
scheduler_locked, my_pthrid );
#endif
pthread_cond_wait( &sched_lock_change, &p2pt_sched_lock );
}
pthread_mutex_unlock( &p2pt_sched_lock );
/*
** Add a cancellation point to this loop, since there are no others.
*/
pthread_testcancel();
pthread_cleanup_pop( 0 );
} while ( got_lock == FALSE );
/*
** task_list_lock prevents other p2pthread pthreads from modifying
** the p2pthread pthread task list while we're searching it and modifying
** the calling task's priority level.
*/
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&task_list_lock );
pthread_mutex_lock( &task_list_lock );
tcb = my_tcb();
if ( tcb != (p2pthread_cb_t *)NULL )
{
pthread_attr_getschedpolicy( &(tcb->attr), &sched_policy );
max_priority = sched_get_priority_max( sched_policy );
((tcb->attr).__schedparam).sched_priority = max_priority;
/*
** Note: here we set the priority for the given pthread.
*/
pthread_setschedparam( tcb->pthrid, sched_policy,
(struct sched_param *)&((tcb->attr).__schedparam) );
}
pthread_mutex_unlock( &task_list_lock );
pthread_cleanup_pop( 0 );
}
/*****************************************************************************
** sched_unlock - 'unlocks the scheduler' to allow preemption of the current
** task by other task-level code. Because we cannot actually lock
** the scheduler in a pthreads environment, the dynamic priority of
** the calling thread was temporarily raised above that of any
** other thread. Therefore, we now restore the priority of the
** calling thread to its original value to 'unlock' the task
** scheduler.
*****************************************************************************/
void
sched_unlock( void )
{
p2pthread_cb_t *tcb;
int sched_policy;
/*
** scheduler_locked ensures that only one p2pthread pthread at a time gets
** to run at max_priority (effectively locking out all other p2pthread
** pthreads). Unlock it here to complete 'unlocking' of the scheduler.
*/
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&p2pt_sched_lock );
pthread_mutex_lock( &p2pt_sched_lock );
if ( scheduler_locked == pthread_self() )
{
if ( sched_lock_level > 0L )
sched_lock_level--;
if ( sched_lock_level < 1L )
{
/*
** task_list_lock prevents other p2pthread pthreads from modifying
** the p2pthread pthread task list while we're searching it and
** modifying the calling task's priority level.
*/
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&task_list_lock );
pthread_mutex_lock( &task_list_lock );
tcb = my_tcb();
if ( tcb != (p2pthread_cb_t *)NULL )
{
pthread_attr_getschedpolicy( &(tcb->attr), &sched_policy );
((tcb->attr).__schedparam).sched_priority =
tcb->prv_priority.sched_priority;
pthread_setschedparam( tcb->pthrid, sched_policy,
(struct sched_param *)&((tcb->attr).__schedparam) );
}
pthread_mutex_unlock( &task_list_lock );
pthread_cleanup_pop( 0 );
scheduler_locked = (pthread_t)NULL;
pthread_cond_broadcast( &sched_lock_change );
}
#ifdef DIAG_PRINTFS
printf( "\r\nsched_unlock sched_lock_level %lu locking tid %ld",
sched_lock_level, scheduler_locked );
#endif
}
#ifdef DIAG_PRINTFS
else
printf( "\r\nsched_unlock locking tid %ld my tid %ld", scheduler_locked,
pthread_self() );
#endif
pthread_mutex_unlock( &p2pt_sched_lock );
pthread_cleanup_pop( 0 );
}
/*****************************************************************************
** link_susp_tcb - appends a new tcb pointer to a linked list of tcb pointers
** for tasks suspended on the object owning the list.
*****************************************************************************/
void
link_susp_tcb( p2pthread_cb_t **list_head, p2pthread_cb_t *new_entry )
{
p2pthread_cb_t *nxt_entry;
if ( list_head != (p2pthread_cb_t **)NULL )
{
pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock,
(void *)&task_list_lock );
pthread_mutex_lock( &task_list_lock );
new_entry->nxt_susp = (p2pthread_cb_t *)NULL;
if ( *list_head != (p2pthread_cb_t *)NULL )
{
for ( nxt_entry = *list_head;
nxt_entry->nxt_susp != (p2pthread_cb_t *)NULL;
nxt_entry = nxt_entry->nxt_susp ) ;
nxt_entry->nxt_susp = new_entry;
#ifdef DIAG_PRINTFS
printf( "\r\nadd susp_tcb @ %p to list @ %p", new_entry,
nxt_entry );
#endif
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -