Skip to content

Commit 04f1fdd

Browse files
authored
Merge pull request nasa#595 from jphickey/fix-456-perflog-race
Fix nasa#456, Perf log threading and concurrency issues
2 parents a77db2b + a268837 commit 04f1fdd

11 files changed

+1042
-357
lines changed
+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/*
2+
** GSC-18128-1, "Core Flight Executive Version 6.7"
3+
**
4+
** Copyright (c) 2006-2019 United States Government as represented by
5+
** the Administrator of the National Aeronautics and Space Administration.
6+
** All Rights Reserved.
7+
**
8+
** Licensed under the Apache License, Version 2.0 (the "License");
9+
** you may not use this file except in compliance with the License.
10+
** You may obtain a copy of the License at
11+
**
12+
** http://www.apache.org/licenses/LICENSE-2.0
13+
**
14+
** Unless required by applicable law or agreed to in writing, software
15+
** distributed under the License is distributed on an "AS IS" BASIS,
16+
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
** See the License for the specific language governing permissions and
18+
** limitations under the License.
19+
*/
20+
21+
/*
22+
** File: cfe_es_backgroundtask.c
23+
**
24+
** Purpose: This file contains the implementation of the ES "background task"
25+
**
26+
** This task sits idle most of the time, but is woken by the ES application
27+
** for various maintenance duties that may take time to execute, such as
28+
** writing status/log files.
29+
**
30+
*/
31+
32+
/*
33+
** Include Section
34+
*/
35+
36+
#include <string.h>
37+
38+
#include "osapi.h"
39+
#include "private/cfe_private.h"
40+
#include "cfe_es_perf.h"
41+
#include "cfe_es_global.h"
42+
#include "cfe_es_task.h"
43+
44+
#define CFE_ES_BACKGROUND_SEM_NAME "ES_BackgroundSem"
45+
#define CFE_ES_BACKGROUND_CHILD_NAME "ES_BackgroundTask"
46+
#define CFE_ES_BACKGROUND_CHILD_STACK_PTR NULL
47+
#define CFE_ES_BACKGROUND_CHILD_STACK_SIZE CFE_PLATFORM_ES_PERF_CHILD_STACK_SIZE
48+
#define CFE_ES_BACKGROUND_CHILD_PRIORITY CFE_PLATFORM_ES_PERF_CHILD_PRIORITY
49+
#define CFE_ES_BACKGROUND_CHILD_FLAGS 0
50+
#define CFE_ES_BACKGROUND_MAX_IDLE_DELAY 30000 /* 30 seconds */
51+
52+
53+
typedef struct
54+
{
55+
bool (*RunFunc)(uint32 ElapsedTime, void *Arg);
56+
void *JobArg;
57+
uint32 ActivePeriod; /**< max wait/delay time between calls when job is active */
58+
uint32 IdlePeriod; /**< max wait/delay time between calls when job is idle */
59+
} CFE_ES_BackgroundJobEntry_t;
60+
61+
/*
62+
* List of "background jobs"
63+
*
64+
* This is just a list of functions to periodically call from the context of the background task,
65+
* and can be added/extended as needed.
66+
*
67+
* Each Job function returns a boolean, and should return "true" if it is active, or "false" if it is idle.
68+
*
69+
* This uses "cooperative multitasking" -- the function should do some limited work, then return to the
70+
* background task. It will be called again after a delay period to do more work.
71+
*/
72+
const CFE_ES_BackgroundJobEntry_t CFE_ES_BACKGROUND_JOB_TABLE[] =
73+
{
74+
{ /* Performance Log Data Dump to file */
75+
.RunFunc = CFE_ES_RunPerfLogDump,
76+
.JobArg = &CFE_ES_TaskData.BackgroundPerfDumpState,
77+
.ActivePeriod = CFE_PLATFORM_ES_PERF_CHILD_MS_DELAY,
78+
.IdlePeriod = CFE_PLATFORM_ES_PERF_CHILD_MS_DELAY * 1000
79+
}
80+
};
81+
82+
#define CFE_ES_BACKGROUND_NUM_JOBS (sizeof(CFE_ES_BACKGROUND_JOB_TABLE) / sizeof(CFE_ES_BACKGROUND_JOB_TABLE[0]))
83+
84+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
85+
/* Name: CFE_ES_BackgroundTask */
86+
/* */
87+
/* Purpose: A helper task for low priority routines that may take time to */
88+
/* execute, such as writing log files. */
89+
/* */
90+
/* Assumptions and Notes: This is started from the ES initialization, and */
91+
/* pends on a semaphore until a work request comes in. This is intended to */
92+
/* avoid the need to create a child task "on demand" when work items arrive, */
93+
/* which is a form of dynamic allocation. */
94+
/* */
95+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
96+
void CFE_ES_BackgroundTask(void)
97+
{
98+
int32 status;
99+
uint32 JobTotal;
100+
uint32 NumJobsRunning;
101+
uint32 NextDelay;
102+
uint32 ElapsedTime;
103+
OS_time_t CurrTime;
104+
OS_time_t LastTime;
105+
const CFE_ES_BackgroundJobEntry_t *JobPtr;
106+
107+
status = CFE_ES_RegisterChildTask();
108+
if (status != CFE_SUCCESS)
109+
{
110+
/* should never occur */
111+
CFE_ES_WriteToSysLog("CFE_ES: Background Task Failed to register: %08lx\n", (unsigned long)status);
112+
return;
113+
}
114+
115+
CFE_PSP_GetTime(&LastTime);
116+
117+
while (true)
118+
{
119+
/*
120+
* compute the elapsed time (difference) between last
121+
* execution and now, in microseconds.
122+
*
123+
* Note this calculation is done as a uint32 which will overflow
124+
* after about 35 minutes, but the max delays ensure that this
125+
* executes at least every few seconds, so that should never happen.
126+
*/
127+
CFE_PSP_GetTime(&CurrTime);
128+
ElapsedTime = 1000000 * (CurrTime.seconds - LastTime.seconds);
129+
ElapsedTime += CurrTime.microsecs;
130+
ElapsedTime -= LastTime.microsecs;
131+
LastTime = CurrTime;
132+
133+
/*
134+
* convert to milliseconds.
135+
* we do not really need high precision
136+
* for background task timings
137+
*/
138+
ElapsedTime /= 1000;
139+
140+
NextDelay = CFE_ES_BACKGROUND_MAX_IDLE_DELAY; /* default; will be adjusted based on active jobs */
141+
JobPtr = CFE_ES_BACKGROUND_JOB_TABLE;
142+
JobTotal = CFE_ES_BACKGROUND_NUM_JOBS;
143+
NumJobsRunning = 0;
144+
145+
while (JobTotal > 0)
146+
{
147+
/*
148+
* call the background job -
149+
* if it returns "true" that means it is active,
150+
* if it returns "false" that means it is idle
151+
*/
152+
if (JobPtr->RunFunc != NULL && JobPtr->RunFunc(ElapsedTime, JobPtr->JobArg))
153+
{
154+
++NumJobsRunning;
155+
156+
if (JobPtr->ActivePeriod != 0 && NextDelay > JobPtr->ActivePeriod)
157+
{
158+
/* next delay is based on this active job wait time */
159+
NextDelay = JobPtr->ActivePeriod;
160+
}
161+
}
162+
else if (JobPtr->IdlePeriod != 0 && NextDelay > JobPtr->IdlePeriod)
163+
{
164+
/* next delay is based on this idle job wait time */
165+
NextDelay = JobPtr->IdlePeriod;
166+
}
167+
--JobTotal;
168+
++JobPtr;
169+
}
170+
171+
CFE_ES_Global.BackgroundTask.NumJobsRunning = NumJobsRunning;
172+
173+
status = OS_BinSemTimedWait(CFE_ES_Global.BackgroundTask.WorkSem, NextDelay);
174+
if (status != OS_SUCCESS && status != OS_SEM_TIMEOUT)
175+
{
176+
/* should never occur */
177+
CFE_ES_WriteToSysLog("CFE_ES: Failed to take background sem: %08lx\n", (unsigned long)status);
178+
break;
179+
}
180+
181+
}
182+
}
183+
184+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
185+
/* Name: CFE_ES_BackgroundInit */
186+
/* */
187+
/* Purpose: Initialize the background task */
188+
/* */
189+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
190+
int32 CFE_ES_BackgroundInit(void)
191+
{
192+
int32 status;
193+
194+
status = OS_BinSemCreate(&CFE_ES_Global.BackgroundTask.WorkSem, CFE_ES_BACKGROUND_SEM_NAME, 0, 0);
195+
if (status != OS_SUCCESS)
196+
{
197+
CFE_ES_WriteToSysLog("CFE_ES: Failed to create background sem: %08lx\n", (unsigned long)status);
198+
return status;
199+
}
200+
201+
/* Spawn a task to write the performance data to a file */
202+
status = CFE_ES_CreateChildTask(&CFE_ES_Global.BackgroundTask.TaskID,
203+
CFE_ES_BACKGROUND_CHILD_NAME,
204+
CFE_ES_BackgroundTask,
205+
CFE_ES_BACKGROUND_CHILD_STACK_PTR,
206+
CFE_ES_BACKGROUND_CHILD_STACK_SIZE,
207+
CFE_ES_BACKGROUND_CHILD_PRIORITY,
208+
CFE_ES_BACKGROUND_CHILD_FLAGS);
209+
210+
if (status != OS_SUCCESS)
211+
{
212+
CFE_ES_WriteToSysLog("CFE_ES: Failed to create background task: %08lx\n", (unsigned long)status);
213+
return status;
214+
}
215+
216+
return CFE_SUCCESS;
217+
}
218+
219+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
220+
/* Name: CFE_ES_BackgroundCleanup */
221+
/* */
222+
/* Purpose: Exit/Stop the background task */
223+
/* */
224+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
225+
void CFE_ES_BackgroundCleanup(void)
226+
{
227+
CFE_ES_DeleteChildTask(CFE_ES_Global.BackgroundTask.TaskID);
228+
OS_BinSemDelete(CFE_ES_Global.BackgroundTask.WorkSem);
229+
230+
CFE_ES_Global.BackgroundTask.TaskID = 0;
231+
CFE_ES_Global.BackgroundTask.WorkSem = 0;
232+
}
233+
234+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
235+
/* Name: CFE_ES_BackgroundWakeup */
236+
/* */
237+
/* Purpose: Wake up the background task */
238+
/* Notifies the background task to perform an extra poll for new work */
239+
/* */
240+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
241+
void CFE_ES_BackgroundWakeup(void)
242+
{
243+
/* wake up the background task by giving the sem.
244+
* This is "informational" and not strictly required,
245+
* but it will make the task immediately wake up and check for new
246+
* work if it was idle. */
247+
OS_BinSemGive(CFE_ES_Global.BackgroundTask.WorkSem);
248+
}
249+
250+

fsw/cfe-core/src/es/cfe_es_global.h

+31-9
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
*/
2020

2121
/*
22-
** File:
23-
** cfe_es_global.h
22+
** File:
23+
** cfe_es_global.h
2424
**
2525
** Purpose:
2626
** This file contains the ES global data definitions.
@@ -66,13 +66,23 @@ typedef struct
6666
{
6767
bool RecordUsed; /* Is the record used(1) or available(0) ? */
6868
uint32 Counter;
69-
char CounterName[OS_MAX_API_NAME]; /* Counter Name */
69+
char CounterName[OS_MAX_API_NAME]; /* Counter Name */
7070
} CFE_ES_GenCounterRecord_t;
7171

72+
/*
73+
* Encapsulates the state of the ES background task
74+
*/
75+
typedef struct
76+
{
77+
uint32 TaskID; /**< OSAL ID of the background task */
78+
uint32 WorkSem; /**< Semaphore that is given whenever background work is pending */
79+
uint32 NumJobsRunning; /**< Current Number of active jobs (updated by background task) */
80+
} CFE_ES_BackgroundTaskState_t;
81+
7282

7383
/*
7484
** Executive Services Global Memory Data
75-
** This is the regular global data that is not preserved on a
85+
** This is the regular global data that is not preserved on a
7686
** processor reset.
7787
*/
7888
typedef struct
@@ -81,20 +91,25 @@ typedef struct
8191
** Debug Variables
8292
*/
8393
CFE_ES_DebugVariables_t DebugVars;
84-
94+
8595
/*
8696
** Shared Data Semaphore
8797
*/
8898
uint32 SharedDataMutex;
89-
99+
100+
/*
101+
** Performance Data Mutex
102+
*/
103+
uint32 PerfDataMutex;
104+
90105
/*
91106
** Startup Sync
92107
*/
93108
uint32 SystemState;
94109

95110
/*
96111
** ES Task Table
97-
*/
112+
*/
98113
uint32 RegisteredTasks;
99114
CFE_ES_TaskRecord_t TaskTable[OS_MAX_TASKS];
100115

@@ -104,7 +119,7 @@ typedef struct
104119
uint32 RegisteredCoreApps;
105120
uint32 RegisteredExternalApps;
106121
CFE_ES_AppRecord_t AppTable[CFE_PLATFORM_ES_MAX_APPLICATIONS];
107-
122+
108123
/*
109124
** ES Shared Library Table
110125
*/
@@ -121,12 +136,19 @@ typedef struct
121136
*/
122137
CFE_ES_CDSVariables_t CDSVars;
123138

139+
/*
140+
* Background task for handling long-running, non real time tasks
141+
* such as maintenance, file writes, and other items.
142+
*/
143+
CFE_ES_BackgroundTaskState_t BackgroundTask;
144+
145+
124146
} CFE_ES_Global_t;
125147

126148
/*
127149
** The Executive Services Global Data declaration
128150
*/
129-
extern CFE_ES_Global_t CFE_ES_Global;
151+
extern CFE_ES_Global_t CFE_ES_Global;
130152

131153
/*
132154
** The Executive Services Nonvolatile Data declaration

0 commit comments

Comments
 (0)