📄 tasklib.c
字号:
/***************************************************************************** * taskLib.c - defines the wrapper functions and data structures needed * to implement a VxWorks task control API in a POSIX Threads * environment. * * Copyright (C) 2000 Monta Vista Software Inc. * * Author : Gary S. Robertson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * 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 for more details. ****************************************************************************/#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 "vxwk2pthread.h"#include "vxwkdefs.h"#undef DIAG_PRINTFS/*** selfRestart is a system function used by a task to restart itself.** The function creates a temporary watchdog timer which restarts ** the terminated task and then deletes itself. */extern void selfRestart( vxwk2pthread_cb_t *restart_tcb );extern BOOL roundRobinIsEnabled( void );/******************************************************************************* VxWorks-to-pthread Global Data Structures*****************************************************************************//*** task_list is a linked list of pthread task control blocks.** It is used to perform en-masse operations on all VxWorks pthread** tasks at once.*/vxwk2pthread_cb_t * task_list = (vxwk2pthread_cb_t *)NULL;/*** task_list_lock is a mutex used to serialize access to the task list*/pthread_mutex_t task_list_lock = PTHREAD_MUTEX_INITIALIZER;/*** vxworks_task_lock is a mutex used to make taskLock exclusive to one** thread at a time.*/pthread_mutex_t vxworks_task_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;/*** taskLock_level tracks recursive nesting levels of taskLock/unlock calls** so the scheduler is only unlocked at the outermost** taskUnlock call.*/static unsigned long taskLock_level = 0;/*** taskLock_change is a condition variable which signals a change from** locked to unlocked or vice-versa.*/static pthread_cond_t taskLock_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_cleanup_pop( 1 ); 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_cleanup_pop( 1 );} /******************************************************************************* my_tcb - returns a pointer to the task control block for the calling task*****************************************************************************/vxwk2pthread_cb_t * my_tcb( void ){ pthread_t my_pthrid; vxwk2pthread_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 != (vxwk2pthread_cb_t *)NULL ) { for ( current_tcb = task_list; current_tcb != (vxwk2pthread_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( (vxwk2pthread_cb_t *)NULL );}/******************************************************************************* tcb_for - returns the address of the task control block for the task** idenified by taskid*****************************************************************************/vxwk2pthread_cb_t * tcb_for( int taskid ){ vxwk2pthread_cb_t *current_tcb; int found_taskid; if ( task_list != (vxwk2pthread_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 != (vxwk2pthread_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 = (vxwk2pthread_cb_t *)NULL; } else current_tcb = (vxwk2pthread_cb_t *)NULL; return( current_tcb );}/******************************************************************************* taskLock - '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 taskLock( void ){ pthread_t my_pthrid; vxwk2pthread_cb_t *tcb; int max_priority, sched_policy, got_lock; /* ** vxworks_task_lock ensures that only one VxWorks pthread at a time gets ** to run at max_priority (effectively locking out all other VxWorks ** 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 taskUnlock() 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 *)&vxworks_task_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( &vxworks_task_lock ); if ( (scheduler_locked == (pthread_t)NULL) || (scheduler_locked == my_pthrid) ) { scheduler_locked = my_pthrid; taskLock_level++; if ( taskLock_level == 0L ) taskLock_level--; got_lock = TRUE; pthread_cond_broadcast( &taskLock_change );#ifdef DIAG_PRINTFS printf( "\r\ntaskLock taskLock_level %lu locking tid %ld", taskLock_level, scheduler_locked );#endif } else {#ifdef DIAG_PRINTFS printf( "\r\ntaskLock locking tid %ld my tid %ld", scheduler_locked, my_pthrid );#endif pthread_cond_wait( &taskLock_change, &vxworks_task_lock ); } pthread_mutex_unlock( &vxworks_task_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 VxWorks pthreads from modifying ** the VxWorks 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 != (vxwk2pthread_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; pthread_setschedparam( tcb->pthrid, sched_policy, (struct sched_param *)&((tcb->attr).__schedparam) ); } pthread_cleanup_pop( 1 );}/******************************************************************************* taskUnlock - '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 taskUnlock( void ){ vxwk2pthread_cb_t *tcb; int sched_policy; /* ** scheduler_locked ensures that only one VxWorks pthread at a time gets ** to run at max_priority (effectively locking out all other VxWorks ** pthreads). Unlock it here to complete 'unlocking' of the scheduler. */ pthread_cleanup_push( (void(*)(void *))pthread_mutex_unlock, (void *)&vxworks_task_lock ); pthread_mutex_lock( &vxworks_task_lock ); if ( scheduler_locked == pthread_self() ) { if ( taskLock_level > 0L ) taskLock_level--; if ( taskLock_level < 1L ) { /* ** task_list_lock prevents other VxWorks pthreads from modifying ** the VxWorks 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 != (vxwk2pthread_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_cleanup_pop( 1 ); scheduler_locked = (pthread_t)NULL; pthread_cond_broadcast( &taskLock_change ); }#ifdef DIAG_PRINTFS printf( "\r\ntaskUnlock taskLock_level %lu locking tid %ld", taskLock_level, scheduler_locked );#endif }#ifdef DIAG_PRINTFS else printf( "\r\ntaskUnlock locking tid %ld my tid %ld", scheduler_locked, pthread_self() );#endif pthread_cleanup_pop( 1 );}/******************************************************************************* 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( vxwk2pthread_cb_t **list_head, vxwk2pthread_cb_t *new_entry ){ vxwk2pthread_cb_t *nxt_entry; if ( list_head != (vxwk2pthread_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 = (vxwk2pthread_cb_t *)NULL; if ( *list_head != (vxwk2pthread_cb_t *)NULL ) { for ( nxt_entry = *list_head; nxt_entry->nxt_susp != (vxwk2pthread_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 { *list_head = new_entry;#ifdef DIAG_PRINTFS printf( "\r\nadd susp_tcb @ %p to list @ %p", new_entry, list_head );#endif } /* ** Initialize the suspended task's pointer back to suspend list ** This is used for cleanup during task deletion. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -