Ref: synchronizing init task with an arbitrary number of worker tasks
Andrei Dimitrief-Jianu
andrei.dimitrief.jianu at gmail.com
Thu Sep 27 18:14:31 UTC 2012
>On Wed, Sep 26, 2012 at 9:05 AM, Joel Sherrill <joel.sherrill at oarcorp.com> wrote:
>> On 09/25/2012 09:22 PM, Andrei Dimitrief-Jianu wrote:
>>
>> Yes, I could use a barrier w/ RTEMS_BARRIER_AUTOMATIC_RELEASE.
>> However, I would prevent from finishing the tasks that I want to
>> synchronize (probably this is the reason why barriers are recommended
>> for the initialization phase). I need something that would let the
>> workers to finish and keep a count of how many of them have finished,
>> and when that count is reached it would release the init task as well.
>>
>> What I was looking for was a synchronization event that could be
>> described by an API similar to:
>>
>> rtems_status_code rtems_synch_event_initialize( rtems_synch_event
>> *synch_event, int wait_count );
>>
>> rtems_status_code rtems_synch_event_wait_one( rtems_synch_event
>> *synch_event );
>>
>> rtems_status_code rtems_synch_event_wait_all( rtems_synch_event
>> *synch_event );
>>
>> rtems_status_code rtems_synch_event_set( rtems_synch_event *synch_event );
>>
>> rtems_status_code rtems_synch_event_reset( rtems_synch_event *synch_event
>> );
>
> I think you are wanting an event set that is independent of a particular
> task. It is an independent object. I have implemented this type of object
> for some non-RTEMS customer specific middleware. It is quite complicated
> to get the semantics correct.
>
> + If you allow posting an event to wake up multiple tasks, then it is O(n)
> when you post an event.
> + There is the issue of whether you allow starvation or scan the entire
> blocking set of tasks to see who might be satisfied.
> + When do events get cleared? When the first blocked task is satisfied
> or when all tasks who might be interested in this task are unblocked?
>
> I would be happy to see something like this implemented in RTEMS and
> have wanted an event manager that is independent of tasks. But getting
> the definition and behaviour correct is not simple.
>
> Another design alternative is to use the classic API event manager
> for up to 32 tasks. Assign an event flag (e.g. bit) to each task. The init
> task will wait for all tasks to post their event (w/ a timeout I hope). The
> application tasks should post their assigned bit when they completed
> initialization. That lets the initialization task know which tasks have
> completed initialization within the allotted time.
>
> To hold the application tasks off until all are finished, they should post
> their event flag to the Init task. Then block on a manual release barrier.
> The application tasks should need to be non-preemptible during
> initialization
> to ensure they post their flag and block atomically.
>
> /* created as non-preemptible, initialization priority */
> rtems_event_send( Init_task_id, my_event_flag );
> rtems_barrier_wait( Init_barrier, forever );
> /* entire application is up, not set our mode/priority to "normal"
> values */
> /* optional */ rtems_task_set_priority( RTEMS_SELF, ... desired run-time
> priority )
> rtems_task_mode( RTEMS_SELF, args for desired preemption mode )
>
> To be completely paranoid, the initialization task could check that the
> count
> at the barrier matches the expectations before releasing them. This gives
> you the detailed information that the global variables solution does and a
> timeout
> capability if all tasks do not complete initialization. If you fail to get
> through
> all the application tasks' initialization, then it should be easy to report
> who
> failed.
>
> --joel
>
There are definitely many ways of getting it done. I have used a
semaphore to synchronize tasks access to the event count variable
(hence the need for a delete method in order to release the resources
used by the semaphore).
I have below an example for the synch_event used with three worker
tasks. It is not yet implemented as a RTEMS manager, but gets the job
done.
Regards,
Andrei.
#include <bsp.h>
#include <stdlib.h>
#include <stdio.h>
/////////////////////////////////////////////////////////////
struct rtems_synch_event
{
uint16_t event_count;
rtems_id semaphore_id;
};
rtems_status_code rtems_synch_event_initialize( struct
rtems_synch_event *synch_event, uint16_t event_count );
rtems_status_code rtems_synch_event_delete( struct rtems_synch_event
*synch_event );
rtems_status_code rtems_synch_event_count( struct rtems_synch_event
*synch_event, uint16_t *event_count );
rtems_status_code rtems_synch_event_wait_one( struct rtems_synch_event
*synch_event );
rtems_status_code rtems_synch_event_wait_all( struct rtems_synch_event
*synch_event );
rtems_status_code rtems_synch_event_set( struct rtems_synch_event
*synch_event );
/////////////////////////////////////////////////////////////
#define WORKER_TASKS 3
rtems_task worker_task( rtems_task_argument argument );
struct rtems_synch_event synch_event;
rtems_task
Init( rtems_task_argument ignored )
{
rtems_id task_id;
rtems_status_code status_code;
rtems_name task_name;
status_code = rtems_synch_event_initialize( &synch_event, WORKER_TASKS );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_synch_event_initialize failed with status of
%d.\n", status_code );
exit( 1 );
}
/////////////////////////////////////////////////////////////////////////////
task_name = rtems_build_name( 'T', 'S', 'K', '1' );
status_code = rtems_task_create(
task_name, 1, RTEMS_MINIMUM_STACK_SIZE,
RTEMS_NO_PREEMPT, RTEMS_FLOATING_POINT, &task_id );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_task_create failed with status of %d.\n", status_code );
exit( 1 );
}
status_code = rtems_task_start( task_id, worker_task, 1 );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_task_start failed with status of %d.\n", status_code );
exit( 1 );
}
/////////////////////////////////////////////////////////////////////////////
task_name = rtems_build_name( 'T', 'S', 'K', '2' );
status_code = rtems_task_create(
task_name, 1, RTEMS_MINIMUM_STACK_SIZE,
RTEMS_NO_PREEMPT, RTEMS_FLOATING_POINT, &task_id );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_task_create failed with status of %d.\n", status_code );
exit( 1 );
}
status_code = rtems_task_start( task_id, worker_task, 2 );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_task_start failed with status of %d.\n", status_code );
exit( 1 );
}
/////////////////////////////////////////////////////////////////////////////
task_name = rtems_build_name( 'T', 'S', 'K', '3' );
status_code = rtems_task_create(
task_name, 1, RTEMS_MINIMUM_STACK_SIZE,
RTEMS_NO_PREEMPT, RTEMS_FLOATING_POINT, &task_id );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_task_create failed with status of %d.\n", status_code );
exit( 1 );
}
status_code = rtems_task_start( task_id, worker_task, 3 );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_task_start failed with status of %d.\n", status_code );
exit( 1 );
}
/////////////////////////////////////////////////////////////////////////////
printf( "\nmain task waiting for the workers to finish...\n" );
rtems_synch_event_wait_all( &synch_event );
status_code = rtems_synch_event_delete( &synch_event );
if( RTEMS_SUCCESSFUL != status_code )
{
printf( "\nrtems_synch_event_delete failed with status of %d.\n",
status_code );
exit( 1 );
}
printf( "\nmain task is exiting...\n" );
exit( 0 );
}
rtems_task
worker_task( rtems_task_argument argument )
{
int index = 0;
while( index++ < 10 * argument )
{
printf( "\ntask [%d] > doing something in the user task [%d].\n",
(uint16_t)argument, index );
rtems_task_wake_after( RTEMS_MILLISECONDS_TO_TICKS( 1000 ) );
}
rtems_synch_event_set( &synch_event );
printf( "\ntask [%d] is exiting...\n", (uint16_t)argument );
rtems_task_delete( rtems_task_self() );
}
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_MICROSECONDS_PER_TICK 1000
#define CONFIGURE_TICKS_PER_TIMESLICE 50
#define CONFIGURE_MAXIMUM_TASKS 4
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>
/////////////////////////////////////////////////////////////
rtems_status_code
rtems_synch_event_initialize( struct rtems_synch_event *synch_event,
uint16_t event_count )
{
rtems_status_code status_code = RTEMS_SUCCESSFUL;
synch_event->event_count = event_count;
status_code = rtems_semaphore_create(
rtems_build_name( 'S', 'Y', 'N' ,'C' ),
1,
RTEMS_LOCAL | RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
RTEMS_LOCAL | RTEMS_NO_INHERIT_PRIORITY,
&synch_event->semaphore_id
);
return status_code;
}
rtems_status_code
rtems_synch_event_delete( struct rtems_synch_event *synch_event )
{
rtems_status_code status_code = RTEMS_SUCCESSFUL;
synch_event->event_count = 0;
status_code = rtems_semaphore_delete( synch_event->semaphore_id );
return status_code;
}
rtems_status_code
rtems_synch_event_count( struct rtems_synch_event *synch_event,
uint16_t *event_count )
{
rtems_status_code status_code = RTEMS_SUCCESSFUL;
status_code = rtems_semaphore_obtain( synch_event->semaphore_id,
RTEMS_WAIT, RTEMS_NO_TIMEOUT );
if( RTEMS_SUCCESSFUL == status_code )
{
*event_count = synch_event->event_count;
status_code = rtems_semaphore_release( synch_event->semaphore_id );
}
return status_code;
}
rtems_status_code
rtems_synch_event_wait_one( struct rtems_synch_event *synch_event )
{
rtems_status_code status_code = RTEMS_SUCCESSFUL;
uint16_t initial_event_count;
status_code = rtems_synch_event_count( synch_event, &initial_event_count );
if( RTEMS_SUCCESSFUL == status_code )
{
uint16_t event_count;
do
{
status_code = rtems_semaphore_obtain( synch_event->semaphore_id,
RTEMS_WAIT, RTEMS_NO_TIMEOUT );
if( RTEMS_SUCCESSFUL == status_code )
{
event_count = synch_event->event_count;
status_code = rtems_semaphore_release( synch_event->semaphore_id );
}
rtems_task_wake_after( CONFIGURE_TICKS_PER_TIMESLICE );
}
while( RTEMS_SUCCESSFUL == status_code && initial_event_count - 1
< event_count );
}
return status_code;
}
rtems_status_code
rtems_synch_event_wait_all( struct rtems_synch_event *synch_event )
{
rtems_status_code status_code = RTEMS_SUCCESSFUL;
uint16_t event_count;
do
{
status_code = rtems_semaphore_obtain( synch_event->semaphore_id,
RTEMS_WAIT, RTEMS_NO_TIMEOUT );
if( RTEMS_SUCCESSFUL == status_code )
{
event_count = synch_event->event_count;
status_code = rtems_semaphore_release( synch_event->semaphore_id );
}
rtems_task_wake_after( CONFIGURE_TICKS_PER_TIMESLICE );
}
while( RTEMS_SUCCESSFUL == status_code && 0 < event_count );
return status_code;
}
rtems_status_code
rtems_synch_event_set( struct rtems_synch_event *synch_event )
{
rtems_status_code status_code = RTEMS_SUCCESSFUL;
status_code = rtems_semaphore_obtain( synch_event->semaphore_id,
RTEMS_WAIT, RTEMS_NO_TIMEOUT );
if( RTEMS_SUCCESSFUL == status_code)
{
synch_event->event_count--;
status_code = rtems_semaphore_release( synch_event->semaphore_id );
}
return status_code;
}
/////////////////////////////////////////////////////////////
More information about the users
mailing list