Compare commits
No commits in common. '6bff8e440dc0b815b2870110a74ff2b9eeb58b4a' and '4b179b96e5624f3d31e4440e2b8bedf35b11a808' have entirely different histories.
6bff8e440d
...
4b179b96e5
38 changed files with 0 additions and 1708 deletions
@ -1,116 +0,0 @@ |
|||||||
#include <stdio.h> |
|
||||||
#include <math.h> |
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
// #define BIG_SIZE (64) // that is 18 446 744 073 709 551 616 paths to check
|
|
||||||
#define BIG_SIZE (32) // that is 4 294 967 295 paths to check, takes about 30 seconds on i7 3.60GHz
|
|
||||||
|
|
||||||
// NOTE: This is probably not the best way to do this.
|
|
||||||
// A tricky case example is:
|
|
||||||
// X = 1, Y = 100,
|
|
||||||
// A = {1, 10, 1, 1, 1},
|
|
||||||
// B = {10, 1, 1, 1, 1000}
|
|
||||||
|
|
||||||
// NOTE: The idea is to start from the end (N-1) and assume that we
|
|
||||||
// have already found the best paths for previous nodes and pick the
|
|
||||||
// lower sum for current nodes. While checking previous nodes the
|
|
||||||
// function is called recursively so it again assumes that previous
|
|
||||||
// nodes are already solved. Since the first pair of nodes are just
|
|
||||||
// integers to be compared, we stop the recursion and the "unwinded"
|
|
||||||
// stack gives us the answer.
|
|
||||||
|
|
||||||
int solution_(int A[], int B[], int N, int X, int Y, char onA) |
|
||||||
{ |
|
||||||
if (N == 0) { |
|
||||||
return onA ? A[0] : B[0]; |
|
||||||
} |
|
||||||
|
|
||||||
int c1, c2; |
|
||||||
if (onA) { |
|
||||||
c1 = 0 + A[N] + solution_(A, B, N-1, X, Y, 1); |
|
||||||
c2 = Y + A[N] + solution_(A, B, N-1, X, Y, 0); |
|
||||||
} |
|
||||||
else { |
|
||||||
c1 = 0 + B[N] + solution_(A, B, N-1, X, Y, 0); |
|
||||||
c2 = X + B[N] + solution_(A, B, N-1, X, Y, 1); |
|
||||||
} |
|
||||||
|
|
||||||
return (c1 <= c2 ? c1 : c2); |
|
||||||
} |
|
||||||
|
|
||||||
int solution(int A[], int B[], int N, int X, int Y) |
|
||||||
{ |
|
||||||
int sa = solution_(A, B, N-1, X, Y, 1); |
|
||||||
int sb = solution_(A, B, N-1, X, Y, 0); |
|
||||||
return (sa <= sb ? sa : sb); |
|
||||||
} |
|
||||||
|
|
||||||
void test(int actual, int expected) |
|
||||||
{ |
|
||||||
if (actual != expected) { |
|
||||||
printf("Test failed, expected %i, got %i\n", expected, actual); |
|
||||||
} |
|
||||||
else { |
|
||||||
printf("Test passed (expected %i, got %i)\n", expected, actual); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int main() |
|
||||||
{ |
|
||||||
{ |
|
||||||
int A[] = {1, 6}; |
|
||||||
int B[] = {3, 2}; |
|
||||||
int N = 2, X = 2, Y = 10; |
|
||||||
test(solution(A, B, N, X, Y), 5); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
int A[] = {1, 6, 2}; |
|
||||||
int B[] = {3, 2, 5}; |
|
||||||
int N = 3, X = 2, Y = 1; |
|
||||||
test(solution(A, B, N, X, Y), 8); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
int A[] = {2, 11, 4, 4}; |
|
||||||
int B[] = {9, 2, 5, 11}; |
|
||||||
int N = 4, X = 8, Y = 4; |
|
||||||
test(solution(A, B, N, X, Y), 21); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
int A[] = {1, 10, 1}; |
|
||||||
int B[] = {10, 1, 10}; |
|
||||||
int N = 3, X = 1, Y = 5; |
|
||||||
test(solution(A, B, N, X, Y), 9); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
int A[] = {8, 3, 3}; |
|
||||||
int B[] = {6, 1, 10}; |
|
||||||
int N = 3, X = 4, Y = 3; |
|
||||||
test(solution(A, B, N, X, Y), 13); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
int A[] = {1, 10, 1, 1}; |
|
||||||
int B[] = {10, 1, 1, 100}; |
|
||||||
int N = 4, X = 1, Y = 100; |
|
||||||
test(solution(A, B, N, X, Y), 13); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
int A[BIG_SIZE]; |
|
||||||
int B[BIG_SIZE]; |
|
||||||
int N = BIG_SIZE, X = 1, Y = 100; |
|
||||||
memset(A, 0, sizeof(A)); |
|
||||||
memset(B, 0, sizeof(A)); |
|
||||||
A[0] = 1; |
|
||||||
B[0] = 10; |
|
||||||
B[BIG_SIZE-1] = 100; |
|
||||||
test(solution(A, B, N, X, Y), 1); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
printf("Done\n"); |
|
||||||
} |
|
||||||
|
Before Width: | Height: | Size: 86 KiB |
@ -1,23 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
project(cpu-tracker LANGUAGES C) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
enable_testing() |
|
||||||
|
|
||||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang") |
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Weverything") |
|
||||||
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") |
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") |
|
||||||
endif() |
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) |
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) |
|
||||||
|
|
||||||
get_filename_component(EXTERN_DIR ${CMAKE_CURRENT_LIST_DIR}/extern ABSOLUTE) |
|
||||||
|
|
||||||
option(BUILD_TESTS "Build tests" ON) |
|
||||||
|
|
||||||
add_subdirectory(app) |
|
||||||
add_subdirectory(lib/thread) |
|
||||||
add_subdirectory(lib/log) |
|
||||||
@ -1,28 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
project(cpu-tracker-app LANGUAGES C VERSION 0.1.0) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
set(TARGET ${PROJECT_NAME}) |
|
||||||
set(OUTPUT_NAME cpu-tracker) |
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
|
||||||
|
|
||||||
configure_file(src/main/Version.h.in Version.h) |
|
||||||
|
|
||||||
add_executable(${TARGET} |
|
||||||
src/main/Main.c |
|
||||||
src/main/CpuTracker.c |
|
||||||
src/core/ProcAnalyzer.c |
|
||||||
src/infrastructure/ProcReader.c |
|
||||||
src/infrastructure/ProcPrinter.c |
|
||||||
) |
|
||||||
|
|
||||||
target_include_directories(${TARGET} PRIVATE src) |
|
||||||
target_link_libraries(${TARGET} log thread) |
|
||||||
set_target_properties(${TARGET} |
|
||||||
PROPERTIES OUTPUT_NAME ${OUTPUT_NAME}) |
|
||||||
|
|
||||||
if (${BUILD_TESTS}) |
|
||||||
add_subdirectory(tests/integration) |
|
||||||
endif() |
|
||||||
@ -1,19 +0,0 @@ |
|||||||
#include "core/ProcAnalyzer.h" |
|
||||||
|
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
void getCpuUsage(double results[], CpuStats stats[], CpuStats prevStats[], uint8_t numCores) |
|
||||||
{ |
|
||||||
// https://rosettacode.org/wiki/Linux_CPU_utilization
|
|
||||||
|
|
||||||
for (int i = 0; i < numCores; i++) { |
|
||||||
uint64_t prevTotal = prevStats[i].user + prevStats[i].nice + prevStats[i].system + prevStats[i].idle; |
|
||||||
uint64_t currentTotal = stats[i].user + stats[i].nice + stats[i].system + stats[i].idle; |
|
||||||
uint64_t totalDiff = currentTotal - prevTotal; |
|
||||||
uint64_t idleDiff = stats[i].idle - prevStats[i].idle; |
|
||||||
results[i] = (1.0 - (double)idleDiff / (double)totalDiff) * 100.0; |
|
||||||
if (results[i] < 0 || isnan(results[i])) { |
|
||||||
results[i] = 0; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,17 +0,0 @@ |
|||||||
#ifndef PROCANALYZER_H |
|
||||||
#define PROCANALYZER_H |
|
||||||
|
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) CpuStats { |
|
||||||
char cpuName[20]; |
|
||||||
uint64_t user; |
|
||||||
uint64_t nice; |
|
||||||
uint64_t system; |
|
||||||
uint64_t idle; |
|
||||||
} CpuStats; |
|
||||||
|
|
||||||
void getCpuUsage(double results[], CpuStats stats[], CpuStats prevStats[], uint8_t numCores); |
|
||||||
|
|
||||||
#endif // PROCANALYZER_H
|
|
||||||
|
|
||||||
@ -1,10 +0,0 @@ |
|||||||
#include "infrastructure/ProcPrinter.h" |
|
||||||
#include <stdio.h> |
|
||||||
|
|
||||||
void printCpuUsage(double results[], uint8_t size) |
|
||||||
{ |
|
||||||
for (uint8_t i = 0; i < size; i++) { |
|
||||||
printf("CPU %d: %f\n", i, results[i]); |
|
||||||
} |
|
||||||
printf("---\n"); |
|
||||||
} |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
#ifndef PROCPRINTER_H |
|
||||||
#define PROCPRINTER_H |
|
||||||
|
|
||||||
#include "core/ProcAnalyzer.h" |
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
void printCpuUsage(double results[], uint8_t size); |
|
||||||
|
|
||||||
#endif // PROCPRINTER_H
|
|
||||||
@ -1,50 +0,0 @@ |
|||||||
#include "infrastructure/ProcReader.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <string.h> |
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
uint8_t countCpuCores(void) |
|
||||||
{ |
|
||||||
uint8_t count = 0; |
|
||||||
char line[256]; |
|
||||||
FILE* fp = fopen("/proc/cpuinfo", "r"); |
|
||||||
if (fp == NULL) { |
|
||||||
fprintf(stderr, "Unable to open /proc/cpuinfo\n"); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fp)) { |
|
||||||
if (strncmp(line, "core id", 7) == 0) { |
|
||||||
++count; |
|
||||||
} |
|
||||||
} |
|
||||||
fclose(fp); |
|
||||||
return count;
|
|
||||||
} |
|
||||||
|
|
||||||
void readCpuStats(CpuStats* stats, uint8_t numCores) |
|
||||||
{ |
|
||||||
// https://www.linuxhowtos.org/System/procstat.htm
|
|
||||||
|
|
||||||
char line[256]; |
|
||||||
FILE* fp = fopen("/proc/stat", "r"); |
|
||||||
if (fp == NULL) { |
|
||||||
fprintf(stderr, "Unable to open /proc/stat\n"); |
|
||||||
exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
fgets(line, sizeof(line), fp); // ignore the first line
|
|
||||||
for (int i = 0; i < numCores; i++) { |
|
||||||
fgets(line, sizeof(line), fp); |
|
||||||
sscanf(line, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
|
|
||||||
stats[i].cpuName, |
|
||||||
&stats[i].user, |
|
||||||
&stats[i].nice, |
|
||||||
&stats[i].system, |
|
||||||
&stats[i].idle); |
|
||||||
} |
|
||||||
|
|
||||||
fclose(fp); |
|
||||||
} |
|
||||||
@ -1,10 +0,0 @@ |
|||||||
#ifndef PROCREADER_H |
|
||||||
#define PROCREADER_H |
|
||||||
|
|
||||||
#include "core/ProcAnalyzer.h" |
|
||||||
|
|
||||||
uint8_t countCpuCores(void); |
|
||||||
|
|
||||||
void readCpuStats(CpuStats* stats, uint8_t numCores); |
|
||||||
|
|
||||||
#endif // PROCREADER_H
|
|
||||||
@ -1,165 +0,0 @@ |
|||||||
#include "CpuTracker.h" |
|
||||||
#include "core/ProcAnalyzer.h" |
|
||||||
#include "infrastructure/ProcReader.h" |
|
||||||
#include "infrastructure/ProcPrinter.h" |
|
||||||
#include "thread/Watchdog.h" |
|
||||||
#include "log/Log.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <stdarg.h> |
|
||||||
|
|
||||||
#define LOG_LINE_MAX 256 |
|
||||||
#define LOG_TAG_MAX 16 |
|
||||||
|
|
||||||
static const int WATCHDOG_INTERVAL_MS = 2000; |
|
||||||
static const int CPU_CHECK_INTERVAL_MS = 1000; |
|
||||||
static const char* LOG_FILE = "/tmp/cpu-monitor.log"; |
|
||||||
|
|
||||||
typedef struct AppContext |
|
||||||
{ |
|
||||||
CpuStats* stats;
|
|
||||||
CpuStats* prevStats; |
|
||||||
Watchdog* watchdog; |
|
||||||
QueuedThread* readProcThread; |
|
||||||
QueuedThread* analyzerThread; |
|
||||||
QueuedThread* printerThread; |
|
||||||
QueuedThread* loggerThread; |
|
||||||
double* results; |
|
||||||
uint8_t numCores; |
|
||||||
BlockerHandle mainBlocker; |
|
||||||
BlockerHandle readBlocker; |
|
||||||
uint8_t reserved[5]; |
|
||||||
} AppContext; |
|
||||||
|
|
||||||
typedef struct LogTuple |
|
||||||
{ |
|
||||||
char tag[LOG_TAG_MAX]; |
|
||||||
char message[LOG_LINE_MAX]; |
|
||||||
} LogTuple; |
|
||||||
|
|
||||||
static AppContext appCtx; |
|
||||||
|
|
||||||
|
|
||||||
_Noreturn static void onThreadUnresponsive(Thread* thread); |
|
||||||
static void threadedLog(const char* tag, const char* format, ...); |
|
||||||
|
|
||||||
static void readProcTask(void* arg); |
|
||||||
static void analyzerTask(void* arg); |
|
||||||
static void printerTask(void* arg); |
|
||||||
static void logTask(void* arg); |
|
||||||
|
|
||||||
|
|
||||||
void initCpuTracker(void) |
|
||||||
{ |
|
||||||
initLogger(LOG_FILE); |
|
||||||
appCtx.numCores = countCpuCores(); |
|
||||||
appCtx.prevStats = calloc(appCtx.numCores, sizeof(CpuStats)); |
|
||||||
appCtx.stats = calloc(appCtx.numCores, sizeof(CpuStats)); |
|
||||||
appCtx.results = calloc(appCtx.numCores, sizeof(double)); |
|
||||||
appCtx.mainBlocker = createBlocker(); |
|
||||||
appCtx.readBlocker = createBlocker(); |
|
||||||
appCtx.watchdog = createWatchdog(WATCHDOG_INTERVAL_MS, |
|
||||||
&onThreadUnresponsive, &threadedLog); |
|
||||||
appCtx.readProcThread = createWatchedThread(appCtx.watchdog); |
|
||||||
appCtx.analyzerThread = createWatchedThread(appCtx.watchdog); |
|
||||||
appCtx.printerThread = createWatchedThread(appCtx.watchdog); |
|
||||||
appCtx.loggerThread = createWatchedThread(appCtx.watchdog); |
|
||||||
} |
|
||||||
|
|
||||||
void runCpuTracker(void) |
|
||||||
{ |
|
||||||
// this task will post itself again until the app is closed
|
|
||||||
postQueuedTask(appCtx.readProcThread, &readProcTask, &appCtx); |
|
||||||
|
|
||||||
startWatchdog(appCtx.watchdog); |
|
||||||
lockBlocker(appCtx.mainBlocker); |
|
||||||
} |
|
||||||
|
|
||||||
void stopCpuTracker(void) |
|
||||||
{ |
|
||||||
notifyBlocker(appCtx.mainBlocker); |
|
||||||
} |
|
||||||
|
|
||||||
void destroyCpuTracker(void) |
|
||||||
{ |
|
||||||
stopWatchdog(appCtx.watchdog); |
|
||||||
notifyBlocker(appCtx.readBlocker); |
|
||||||
|
|
||||||
joinWatchedThreads(appCtx.watchdog); |
|
||||||
freeWatchedThreads(appCtx.watchdog); |
|
||||||
freeWatchdog(&(appCtx.watchdog)); |
|
||||||
freeBlocker(&(appCtx.mainBlocker)); |
|
||||||
freeBlocker(&(appCtx.readBlocker)); |
|
||||||
free(appCtx.results); |
|
||||||
free(appCtx.prevStats); |
|
||||||
free(appCtx.stats); |
|
||||||
closeLogger(); |
|
||||||
} |
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
_Noreturn static void onThreadUnresponsive(Thread* thread) |
|
||||||
{ |
|
||||||
printf("Thread %p is unresponsive, exiting\n", (void*)(thread)); |
|
||||||
usleep(1000 * 1000); |
|
||||||
_exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
static void readProcTask(void* arg) |
|
||||||
{ |
|
||||||
(void)arg; |
|
||||||
readCpuStats(appCtx.stats, appCtx.numCores); |
|
||||||
|
|
||||||
if (appCtx.prevStats[0].user != 0) { |
|
||||||
getCpuUsage(appCtx.results, appCtx.stats, appCtx.prevStats, appCtx.numCores); |
|
||||||
// NOTE: a mutex could be used here, but since the analyzer thread is idle
|
|
||||||
// until notified and we assume t(analyzer) < t(read) (including interval
|
|
||||||
// in the latter) there will be no data race.
|
|
||||||
postQueuedTask(appCtx.analyzerThread, &analyzerTask, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
memcpy(appCtx.prevStats, appCtx.stats, appCtx.numCores * sizeof(appCtx.stats)); |
|
||||||
lockBlockerTimed(appCtx.readBlocker, CPU_CHECK_INTERVAL_MS); |
|
||||||
postQueuedTask(appCtx.readProcThread, &readProcTask, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
static void analyzerTask(void* arg) |
|
||||||
{ |
|
||||||
(void)arg; |
|
||||||
threadedLog("analyzer", "Processing results"); |
|
||||||
getCpuUsage(appCtx.results, appCtx.stats, appCtx.prevStats, appCtx.numCores); |
|
||||||
postQueuedTask(appCtx.printerThread, &printerTask, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
static void printerTask(void* arg) |
|
||||||
{ |
|
||||||
(void)arg; |
|
||||||
threadedLog("printer", "Printing results"); |
|
||||||
printCpuUsage(appCtx.results, appCtx.numCores); |
|
||||||
} |
|
||||||
|
|
||||||
static void logTask(void* arg) |
|
||||||
{ |
|
||||||
LogTuple* tuple = (LogTuple*)arg; |
|
||||||
writeLog(tuple->tag, tuple->message); |
|
||||||
free(tuple); |
|
||||||
} |
|
||||||
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0))) |
|
||||||
static void threadedLog(const char* tag, const char* format, ...) |
|
||||||
{ |
|
||||||
LogTuple* log = NULL; |
|
||||||
va_list args; |
|
||||||
if (!appCtx.watchdog->running) { // stop posting tasks if the application is exiting
|
|
||||||
return; |
|
||||||
} |
|
||||||
log = calloc(1, sizeof(LogTuple)); |
|
||||||
va_start(args, format); |
|
||||||
vsprintf((char*)log->message, format, args); |
|
||||||
va_end(args); |
|
||||||
strncpy(log->tag, tag, LOG_TAG_MAX); |
|
||||||
postQueuedTask(appCtx.loggerThread, &logTask, log); |
|
||||||
} |
|
||||||
@ -1,12 +0,0 @@ |
|||||||
#ifndef CPUTRACKER_H |
|
||||||
#define CPUTRACKER_H |
|
||||||
|
|
||||||
void initCpuTracker(void); |
|
||||||
|
|
||||||
void runCpuTracker(void); |
|
||||||
|
|
||||||
void stopCpuTracker(void); |
|
||||||
|
|
||||||
void destroyCpuTracker(void); |
|
||||||
|
|
||||||
#endif // CPUTRACKER_H
|
|
||||||
@ -1,29 +0,0 @@ |
|||||||
#include "Version.h" |
|
||||||
#include "CpuTracker.h" |
|
||||||
|
|
||||||
#include <signal.h> |
|
||||||
|
|
||||||
#define VERSION_BUFFER_SIZE (16) |
|
||||||
|
|
||||||
static void onSigInt(int sig) |
|
||||||
{ |
|
||||||
(void)sig; |
|
||||||
printf("\nSIGINT received, exiting\n"); |
|
||||||
stopCpuTracker(); |
|
||||||
} |
|
||||||
|
|
||||||
int main(void) |
|
||||||
{ |
|
||||||
{ |
|
||||||
char vBuf[VERSION_BUFFER_SIZE]; |
|
||||||
printf("CPU monitor %s\n", getVersionString(vBuf)); |
|
||||||
} |
|
||||||
|
|
||||||
signal(SIGINT, onSigInt); |
|
||||||
|
|
||||||
initCpuTracker(); |
|
||||||
runCpuTracker(); |
|
||||||
printf("Stopping...\n"); |
|
||||||
destroyCpuTracker(); |
|
||||||
printf("Done.\n"); |
|
||||||
} |
|
||||||
@ -1,19 +0,0 @@ |
|||||||
#ifndef VERSION_H_IN |
|
||||||
#define VERSION_H_IN |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
static const uint8_t VERSION_MAJOR = ${PROJECT_VERSION_MAJOR}; |
|
||||||
static const uint8_t VERSION_MINOR = ${PROJECT_VERSION_MINOR}; |
|
||||||
static const uint8_t VERSION_PATCH = ${PROJECT_VERSION_PATCH}; |
|
||||||
static const char* VERSION_SUFFIX = "${PROJECT_VERSION_SUFFIX}"; |
|
||||||
|
|
||||||
static char* getVersionString(char* buffer) { |
|
||||||
sprintf(buffer, "%d.%d.%d%s", |
|
||||||
VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, |
|
||||||
VERSION_SUFFIX); |
|
||||||
return buffer; |
|
||||||
} |
|
||||||
|
|
||||||
#endif // VERSION_H_IN
|
|
||||||
@ -1,16 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
enable_testing() |
|
||||||
|
|
||||||
set(SRC_DIR ../../src) |
|
||||||
|
|
||||||
include_directories(${SRC_DIR}) |
|
||||||
|
|
||||||
add_executable(ProcReaderTest |
|
||||||
${SRC_DIR}/core/ProcAnalyzer.c |
|
||||||
${SRC_DIR}/infrastructure/ProcReader.c |
|
||||||
${SRC_DIR}/infrastructure/ProcPrinter.c |
|
||||||
ProcReader.test.c |
|
||||||
) |
|
||||||
add_test(ProcReaderTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ProcReaderTest) |
|
||||||
@ -1,35 +0,0 @@ |
|||||||
#include "infrastructure/ProcReader.h" |
|
||||||
#include "infrastructure/ProcPrinter.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <string.h> |
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
int main() { |
|
||||||
uint8_t numCores = countCpuCores(); |
|
||||||
|
|
||||||
CpuStats* prevStats = calloc(numCores, sizeof(CpuStats)); |
|
||||||
CpuStats* currentStats = calloc(numCores, sizeof(CpuStats)); |
|
||||||
|
|
||||||
double* results = calloc(numCores, sizeof(double)); |
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
readCpuStats(currentStats, numCores); |
|
||||||
|
|
||||||
if (prevStats[0].user != 0) { |
|
||||||
getCpuUsage(results, currentStats, prevStats, numCores); |
|
||||||
printCpuUsage(results, numCores); |
|
||||||
} |
|
||||||
|
|
||||||
memcpy(prevStats, currentStats, numCores * sizeof(currentStats)); |
|
||||||
|
|
||||||
usleep(500 * 1000); |
|
||||||
} |
|
||||||
|
|
||||||
free(results); |
|
||||||
free(prevStats); |
|
||||||
free(currentStats); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
@ -1,24 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
project(log LANGUAGES C VERSION 0.1.0) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
set(TARGET ${PROJECT_NAME}) |
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
|
||||||
|
|
||||||
add_library(${TARGET} |
|
||||||
src/Log.c |
|
||||||
) |
|
||||||
|
|
||||||
target_include_directories(${TARGET} |
|
||||||
PRIVATE |
|
||||||
src |
|
||||||
PUBLIC |
|
||||||
include |
|
||||||
) |
|
||||||
|
|
||||||
if (${BUILD_TESTS}) |
|
||||||
add_subdirectory(tests/integration) |
|
||||||
endif() |
|
||||||
|
|
||||||
@ -1,12 +0,0 @@ |
|||||||
#ifndef LOG_H |
|
||||||
#define LOG_H |
|
||||||
|
|
||||||
void initLogger(const char* logfile); |
|
||||||
|
|
||||||
void clearLogs(void); |
|
||||||
|
|
||||||
void writeLog(const char* tag, const char* format, ...); |
|
||||||
|
|
||||||
void closeLogger(void); |
|
||||||
|
|
||||||
#endif // LOG_H
|
|
||||||
@ -1,62 +0,0 @@ |
|||||||
#include "log/Log.h" |
|
||||||
|
|
||||||
#include <string.h> |
|
||||||
#include <time.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <stdarg.h> |
|
||||||
|
|
||||||
#define LOG_FILE_PATH_MAX 256 |
|
||||||
// #define LOG_LINE_MAX 256
|
|
||||||
|
|
||||||
static FILE* logFile = NULL; |
|
||||||
static char logFilePath[LOG_FILE_PATH_MAX]; |
|
||||||
|
|
||||||
void initLogger(const char* logfile) |
|
||||||
{ |
|
||||||
strcpy(logFilePath, logfile); |
|
||||||
if (logFile != NULL) { |
|
||||||
fclose(logFile); |
|
||||||
} |
|
||||||
logFile = fopen(logFilePath, "a"); |
|
||||||
if (logFile == NULL) { |
|
||||||
fprintf(stderr, "Unable to open logfile\n"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void clearLogs(void) |
|
||||||
{ |
|
||||||
if (logFile != NULL) { |
|
||||||
closeLogger(); |
|
||||||
} |
|
||||||
if (remove(logFilePath) != 0) { |
|
||||||
fprintf(stderr, "Unable to remove logfile\n"); |
|
||||||
} |
|
||||||
initLogger(logFilePath); |
|
||||||
} |
|
||||||
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0))) |
|
||||||
void writeLog(const char* tag, const char* format, ...) |
|
||||||
{ |
|
||||||
time_t now = time(NULL); |
|
||||||
struct tm* timeinfo = localtime(&now); |
|
||||||
char timestamp[20]; |
|
||||||
va_list args; |
|
||||||
|
|
||||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", timeinfo); |
|
||||||
|
|
||||||
va_start(args, format); |
|
||||||
fprintf(logFile, "%s %s ", timestamp, tag); |
|
||||||
vfprintf(logFile, format, args); |
|
||||||
fprintf(logFile, "\n"); |
|
||||||
va_end(args); |
|
||||||
|
|
||||||
fflush(logFile); |
|
||||||
} |
|
||||||
|
|
||||||
void closeLogger(void) |
|
||||||
{ |
|
||||||
if (logFile != NULL) { |
|
||||||
fclose(logFile); |
|
||||||
logFile = NULL; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,12 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
enable_testing() |
|
||||||
|
|
||||||
set(SRC_DIR ../../src) |
|
||||||
|
|
||||||
include_directories(${SRC_DIR}) |
|
||||||
|
|
||||||
add_executable(LogTest Log.test.c) |
|
||||||
target_link_libraries(LogTest log) |
|
||||||
add_test(LogTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/LogTest) |
|
||||||
@ -1,54 +0,0 @@ |
|||||||
#include "log/Log.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <assert.h> |
|
||||||
|
|
||||||
#define TAG "TEST" |
|
||||||
|
|
||||||
static const char* LOG_FILE = "/tmp/log-test.log"; |
|
||||||
|
|
||||||
static int countLines(const char* filePath) { |
|
||||||
int count = 0; |
|
||||||
int ch; |
|
||||||
FILE* file = fopen(filePath, "r"); |
|
||||||
if (file == NULL) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
while ((ch = fgetc(file)) != EOF) { |
|
||||||
if (ch == '\n') { |
|
||||||
count++; |
|
||||||
} |
|
||||||
} |
|
||||||
fclose(file); |
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int main(void) |
|
||||||
{ |
|
||||||
int lineCount = 0; |
|
||||||
|
|
||||||
initLogger(LOG_FILE); |
|
||||||
clearLogs(); |
|
||||||
|
|
||||||
lineCount = countLines(LOG_FILE); |
|
||||||
assert(lineCount == 0); |
|
||||||
|
|
||||||
writeLog(TAG, "First log message"); |
|
||||||
writeLog(TAG, "Second log message"); |
|
||||||
writeLog(TAG, "Third log message"); |
|
||||||
|
|
||||||
lineCount = countLines(LOG_FILE); |
|
||||||
assert(lineCount == 3); |
|
||||||
|
|
||||||
writeLog(TAG, "This is %ith line that is %s", 4, "formatted"); |
|
||||||
|
|
||||||
lineCount = countLines(LOG_FILE); |
|
||||||
assert(lineCount == 4); |
|
||||||
|
|
||||||
closeLogger(); |
|
||||||
|
|
||||||
printf("Log file %s contains %d lines\n", LOG_FILE, lineCount); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
@ -1,29 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
project(thread LANGUAGES C VERSION 0.1.0) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
set(TARGET ${PROJECT_NAME}) |
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
|
||||||
enable_testing() |
|
||||||
|
|
||||||
add_library(${TARGET} |
|
||||||
src/core/TaskQueue.c |
|
||||||
src/infrastructure/Thread.c |
|
||||||
src/infrastructure/QueuedThread.c |
|
||||||
src/infrastructure/Watchdog.c |
|
||||||
src/infrastructure/PtBlocker.c |
|
||||||
) |
|
||||||
|
|
||||||
target_include_directories(${TARGET} |
|
||||||
PRIVATE |
|
||||||
src |
|
||||||
PUBLIC |
|
||||||
include |
|
||||||
) |
|
||||||
|
|
||||||
if (${BUILD_TESTS}) |
|
||||||
add_subdirectory(tests/unit) |
|
||||||
add_subdirectory(tests/integration) |
|
||||||
endif() |
|
||||||
|
|
||||||
@ -1,22 +0,0 @@ |
|||||||
#ifndef BLOCKER_H |
|
||||||
#define BLOCKER_H |
|
||||||
|
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
typedef int8_t BlockerHandle; |
|
||||||
typedef void(*LockBlockerFn)(BlockerHandle); |
|
||||||
typedef void(*NotifyBlockerFn)(BlockerHandle); |
|
||||||
|
|
||||||
enum PTB_TIMEOUT_STATUS {PTB_TIMEOUT, PTB_NO_TIMEOUT}; |
|
||||||
|
|
||||||
BlockerHandle createBlocker(void); |
|
||||||
|
|
||||||
void lockBlocker(BlockerHandle id); |
|
||||||
|
|
||||||
enum PTB_TIMEOUT_STATUS lockBlockerTimed(BlockerHandle id, uint32_t timeoutMs); |
|
||||||
|
|
||||||
void notifyBlocker(BlockerHandle id); |
|
||||||
|
|
||||||
void freeBlocker(BlockerHandle* id); |
|
||||||
|
|
||||||
#endif // BLOCKER_H
|
|
||||||
@ -1,26 +0,0 @@ |
|||||||
#ifndef QUEUEDTHREAD_H |
|
||||||
#define QUEUEDTHREAD_H |
|
||||||
|
|
||||||
#include "thread/Blocker.h" |
|
||||||
#include "thread/TaskQueue.h" |
|
||||||
#include "thread/Thread.h" |
|
||||||
|
|
||||||
typedef struct QueuedThread |
|
||||||
{ |
|
||||||
TaskQueue* taskQueue; |
|
||||||
BlockerHandle blocker; |
|
||||||
uint8_t reserved[sizeof(void*)-sizeof(BlockerHandle)]; |
|
||||||
Thread* thread; |
|
||||||
} QueuedThread; |
|
||||||
|
|
||||||
|
|
||||||
QueuedThread* createQueuedThread(void); |
|
||||||
|
|
||||||
void postQueuedTask(QueuedThread* qthread, Task task, void* arg); |
|
||||||
|
|
||||||
void joinQueuedThread(QueuedThread* qthread); |
|
||||||
|
|
||||||
void freeQueuedThread(QueuedThread** qthread); |
|
||||||
|
|
||||||
|
|
||||||
#endif // QUEUEDTHREAD_H
|
|
||||||
@ -1,42 +0,0 @@ |
|||||||
#ifndef TASKQUEUE_H |
|
||||||
#define TASKQUEUE_H |
|
||||||
|
|
||||||
#include "Blocker.h" |
|
||||||
#include <inttypes.h> |
|
||||||
#include <stdbool.h> |
|
||||||
|
|
||||||
typedef void(*Task)(void*); |
|
||||||
|
|
||||||
typedef struct TaskEntry { |
|
||||||
Task task; |
|
||||||
void* data; |
|
||||||
struct TaskEntry* next; |
|
||||||
} TaskEntry; |
|
||||||
|
|
||||||
typedef struct TaskQueue { |
|
||||||
TaskEntry* front; |
|
||||||
TaskEntry* back; |
|
||||||
NotifyBlockerFn notify; |
|
||||||
LockBlockerFn lock; |
|
||||||
BlockerHandle blocker; |
|
||||||
bool running; |
|
||||||
uint8_t reserved[6]; |
|
||||||
} TaskQueue; |
|
||||||
|
|
||||||
typedef struct TaskQueue* TaskQueuePtr; |
|
||||||
|
|
||||||
TaskQueuePtr createTaskQueue(BlockerHandle blocker, LockBlockerFn lock, NotifyBlockerFn notify); |
|
||||||
|
|
||||||
void enqueueTask(TaskQueuePtr queue, Task task, void* data); |
|
||||||
|
|
||||||
TaskEntry* dequeueTask(TaskQueuePtr queue); |
|
||||||
|
|
||||||
void runQueue(TaskQueuePtr queue); |
|
||||||
|
|
||||||
void stopQueue(TaskQueuePtr queue); |
|
||||||
|
|
||||||
void freeTask(TaskEntry** entry); |
|
||||||
|
|
||||||
void freeTaskQueue(TaskQueuePtr* queue); |
|
||||||
|
|
||||||
#endif // TASKQUEUE_H
|
|
||||||
@ -1,21 +0,0 @@ |
|||||||
#ifndef THREAD_H |
|
||||||
#define THREAD_H |
|
||||||
|
|
||||||
typedef void*(*ThreadFn)(void*); |
|
||||||
|
|
||||||
typedef struct Thread |
|
||||||
{ |
|
||||||
void* impl; |
|
||||||
} Thread; |
|
||||||
|
|
||||||
|
|
||||||
Thread* createThread(ThreadFn threadFn, void* arg); |
|
||||||
|
|
||||||
void joinThread(Thread* thread); |
|
||||||
|
|
||||||
void killThread(Thread* thread); |
|
||||||
|
|
||||||
void freeThread(Thread** thread); |
|
||||||
|
|
||||||
|
|
||||||
#endif // THREAD_H
|
|
||||||
@ -1,39 +0,0 @@ |
|||||||
#ifndef WATCHDOG_H |
|
||||||
#define WATCHDOG_H |
|
||||||
|
|
||||||
#include "thread/QueuedThread.h" |
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
typedef void(*WatchdogEventHandler)(Thread*); |
|
||||||
|
|
||||||
typedef void(*LogFn)(const char* tag, const char* format, ...); |
|
||||||
|
|
||||||
typedef struct Watchdog |
|
||||||
{ |
|
||||||
Thread* watchingThread; |
|
||||||
void* threads; |
|
||||||
WatchdogEventHandler eventHandler; |
|
||||||
LogFn log; |
|
||||||
uint32_t intervalMs; |
|
||||||
bool running; |
|
||||||
BlockerHandle blocker; |
|
||||||
uint8_t reserved[2]; |
|
||||||
} Watchdog; |
|
||||||
|
|
||||||
Watchdog* createWatchdog(uint32_t intervalMs, WatchdogEventHandler eventHandler, LogFn logFn); |
|
||||||
|
|
||||||
QueuedThread* createWatchedThread(Watchdog* watchdog); |
|
||||||
|
|
||||||
void startWatchdog(Watchdog* watchdog); |
|
||||||
|
|
||||||
void startWatchdogSync(Watchdog* watchdog); |
|
||||||
|
|
||||||
void stopWatchdog(Watchdog* watchdog); |
|
||||||
|
|
||||||
void joinWatchedThreads(Watchdog* watchdog); |
|
||||||
|
|
||||||
void freeWatchedThreads(Watchdog* watchdog); |
|
||||||
|
|
||||||
void freeWatchdog(Watchdog** watchdog); |
|
||||||
|
|
||||||
#endif // WATCHDOG_H
|
|
||||||
@ -1,95 +0,0 @@ |
|||||||
#include "thread/TaskQueue.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
|
|
||||||
inline static void runUntilEmpty(TaskQueuePtr queue) { |
|
||||||
TaskEntry* current = NULL; |
|
||||||
for (current = dequeueTask(queue); |
|
||||||
current != NULL; |
|
||||||
current = dequeueTask(queue)) { |
|
||||||
if (queue->running) { // any task could've changed it
|
|
||||||
current->task(current->data); |
|
||||||
} |
|
||||||
freeTask(¤t); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
TaskQueuePtr createTaskQueue(BlockerHandle blocker, LockBlockerFn lock, NotifyBlockerFn notify) |
|
||||||
{ |
|
||||||
TaskQueuePtr queue = calloc(1, sizeof(TaskQueue)); |
|
||||||
queue->front = NULL; |
|
||||||
queue->back = NULL; |
|
||||||
queue->blocker = blocker; |
|
||||||
queue->lock = lock; |
|
||||||
queue->notify = notify; |
|
||||||
queue->running = false; |
|
||||||
return queue; |
|
||||||
} |
|
||||||
|
|
||||||
void enqueueTask(TaskQueuePtr queue, Task task, void* data) |
|
||||||
{ |
|
||||||
TaskEntry* newEntry = (TaskEntry*)calloc(1, sizeof(TaskEntry)); |
|
||||||
|
|
||||||
newEntry->task = task; |
|
||||||
newEntry->data = data; |
|
||||||
newEntry->next = NULL; |
|
||||||
|
|
||||||
if (queue->front == NULL) { |
|
||||||
queue->front = newEntry; |
|
||||||
queue->back = newEntry; |
|
||||||
} else { |
|
||||||
queue->back->next = newEntry; |
|
||||||
queue->back = newEntry; |
|
||||||
} |
|
||||||
queue->notify(queue->blocker); |
|
||||||
} |
|
||||||
|
|
||||||
TaskEntry* dequeueTask(TaskQueuePtr queue) |
|
||||||
{ |
|
||||||
TaskEntry* current = NULL; |
|
||||||
if (queue->front == NULL) { |
|
||||||
return current; |
|
||||||
} |
|
||||||
current = queue->front; |
|
||||||
queue->front = current->next; |
|
||||||
if (queue->front == NULL) { |
|
||||||
queue->back = NULL; |
|
||||||
} |
|
||||||
return current; |
|
||||||
} |
|
||||||
|
|
||||||
void runQueue(TaskQueuePtr queue) |
|
||||||
{ |
|
||||||
queue->running = true; |
|
||||||
while (queue->running) { |
|
||||||
runUntilEmpty(queue); |
|
||||||
if (queue->running) { |
|
||||||
queue->lock(queue->blocker); |
|
||||||
} |
|
||||||
} |
|
||||||
// execute remaining tasks
|
|
||||||
runUntilEmpty(queue); |
|
||||||
} |
|
||||||
|
|
||||||
void stopQueue(TaskQueuePtr queue) |
|
||||||
{ |
|
||||||
queue->running = false; |
|
||||||
queue->notify(queue->blocker); |
|
||||||
} |
|
||||||
|
|
||||||
void freeTask(TaskEntry** entry) |
|
||||||
{ |
|
||||||
free(*entry); |
|
||||||
*entry = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
void freeTaskQueue(TaskQueuePtr* queue) |
|
||||||
{ |
|
||||||
TaskEntry* task = dequeueTask(*queue); |
|
||||||
while (task != NULL) { |
|
||||||
freeTask(&task); |
|
||||||
task = dequeueTask(*queue); |
|
||||||
} |
|
||||||
free(*queue); |
|
||||||
*queue = NULL; |
|
||||||
} |
|
||||||
@ -1,76 +0,0 @@ |
|||||||
#include "thread/Blocker.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <pthread.h> |
|
||||||
#include <time.h> |
|
||||||
|
|
||||||
#ifndef BLOCKER_COUNT_MAX |
|
||||||
#define BLOCKER_COUNT_MAX 8 |
|
||||||
#endif |
|
||||||
|
|
||||||
typedef struct PtBlocker |
|
||||||
{ |
|
||||||
pthread_mutex_t mutex; |
|
||||||
pthread_cond_t cond; |
|
||||||
} PtBlocker; |
|
||||||
|
|
||||||
// TODO: dynamic list?
|
|
||||||
static PtBlocker* blockers[BLOCKER_COUNT_MAX] = {NULL}; |
|
||||||
|
|
||||||
BlockerHandle createBlocker(void) |
|
||||||
{ |
|
||||||
BlockerHandle id = 0; |
|
||||||
while (blockers[id] != NULL && id < BLOCKER_COUNT_MAX) { |
|
||||||
++id; |
|
||||||
} |
|
||||||
if (id == BLOCKER_COUNT_MAX) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
blockers[id] = calloc(1, sizeof(PtBlocker)); |
|
||||||
return id; |
|
||||||
} |
|
||||||
|
|
||||||
void lockBlocker(BlockerHandle id) |
|
||||||
{ |
|
||||||
pthread_mutex_lock(&blockers[id]->mutex); |
|
||||||
pthread_cond_wait(&blockers[id]->cond, &blockers[id]->mutex); |
|
||||||
pthread_mutex_unlock(&blockers[id]->mutex); |
|
||||||
} |
|
||||||
|
|
||||||
enum PTB_TIMEOUT_STATUS lockBlockerTimed(BlockerHandle id, uint32_t timeoutMs) |
|
||||||
{ |
|
||||||
enum PTB_TIMEOUT_STATUS status = PTB_TIMEOUT; |
|
||||||
int waitResult = 0; |
|
||||||
struct timespec timeout; |
|
||||||
clock_gettime(CLOCK_REALTIME, &timeout); |
|
||||||
timeout.tv_sec += timeoutMs / 1000; |
|
||||||
timeout.tv_nsec += (timeoutMs % 1000) * 1000000; |
|
||||||
|
|
||||||
pthread_mutex_lock(&blockers[id]->mutex); |
|
||||||
waitResult = pthread_cond_timedwait(&blockers[id]->cond, &blockers[id]->mutex, &timeout); |
|
||||||
if (waitResult == 0) { |
|
||||||
status = PTB_NO_TIMEOUT; |
|
||||||
} |
|
||||||
pthread_mutex_unlock(&blockers[id]->mutex); |
|
||||||
return status; |
|
||||||
} |
|
||||||
|
|
||||||
void notifyBlocker(BlockerHandle id) |
|
||||||
{ |
|
||||||
pthread_mutex_lock(&blockers[id]->mutex); |
|
||||||
pthread_cond_signal(&blockers[id]->cond); |
|
||||||
pthread_mutex_unlock(&blockers[id]->mutex); |
|
||||||
} |
|
||||||
|
|
||||||
void freeBlocker(BlockerHandle* id) |
|
||||||
{ |
|
||||||
if (*id == -1) { |
|
||||||
return; |
|
||||||
} |
|
||||||
pthread_mutex_destroy(&blockers[*id]->mutex); |
|
||||||
pthread_cond_destroy(&blockers[*id]->cond); |
|
||||||
free(blockers[*id]); |
|
||||||
blockers[*id] = NULL; |
|
||||||
*id = -1; |
|
||||||
} |
|
||||||
@ -1,44 +0,0 @@ |
|||||||
#include "thread/QueuedThread.h" |
|
||||||
#include "thread/Blocker.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
|
|
||||||
static void* queueThread(void* arg) |
|
||||||
{ |
|
||||||
QueuedThread* qthread = (QueuedThread*)arg; |
|
||||||
runQueue(qthread->taskQueue); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
QueuedThread* createQueuedThread(void) |
|
||||||
{ |
|
||||||
QueuedThread* qthread = calloc(1, sizeof(QueuedThread)); |
|
||||||
qthread->blocker = createBlocker(); |
|
||||||
qthread->taskQueue = createTaskQueue(qthread->blocker, &lockBlocker, ¬ifyBlocker); |
|
||||||
qthread->thread = createThread(&queueThread, qthread); |
|
||||||
return qthread; |
|
||||||
} |
|
||||||
|
|
||||||
void postQueuedTask(QueuedThread* qthread, Task task, void* arg) |
|
||||||
{ |
|
||||||
enqueueTask(qthread->taskQueue, task, arg); |
|
||||||
} |
|
||||||
|
|
||||||
void joinQueuedThread(QueuedThread* qthread) |
|
||||||
{ |
|
||||||
stopQueue(qthread->taskQueue); |
|
||||||
joinThread(qthread->thread); |
|
||||||
} |
|
||||||
|
|
||||||
void freeQueuedThread(QueuedThread** qthread) |
|
||||||
{ |
|
||||||
QueuedThread* t = *qthread; |
|
||||||
freeTaskQueue(&(t->taskQueue)); |
|
||||||
freeBlocker(&(t->blocker)); |
|
||||||
freeThread(&(t->thread)); |
|
||||||
free(t); |
|
||||||
|
|
||||||
*qthread = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
@ -1,43 +0,0 @@ |
|||||||
#include "thread/Thread.h" |
|
||||||
|
|
||||||
#include <pthread.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <signal.h> |
|
||||||
|
|
||||||
typedef struct ThreadPthreadImpl |
|
||||||
{ |
|
||||||
pthread_t thread; |
|
||||||
} ThreadPthreadImpl; |
|
||||||
|
|
||||||
|
|
||||||
Thread* createThread(ThreadFn threadFn, void* arg) |
|
||||||
{ |
|
||||||
ThreadPthreadImpl* pthreadImpl = NULL; |
|
||||||
Thread* thread = calloc(1, sizeof(Thread)); |
|
||||||
thread->impl = calloc(1, sizeof(ThreadPthreadImpl)); |
|
||||||
pthreadImpl = (ThreadPthreadImpl*)thread->impl; |
|
||||||
if (pthread_create(&(pthreadImpl->thread), NULL, threadFn, arg) != 0) { |
|
||||||
printf("Thread: Failed to create thread.\n"); |
|
||||||
} |
|
||||||
return thread; |
|
||||||
} |
|
||||||
|
|
||||||
void joinThread(Thread* thread) |
|
||||||
{ |
|
||||||
ThreadPthreadImpl* pthreadImpl = (ThreadPthreadImpl*)thread->impl; |
|
||||||
pthread_join(pthreadImpl->thread, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
void killThread(Thread* thread) |
|
||||||
{ |
|
||||||
ThreadPthreadImpl* pthreadImpl = (ThreadPthreadImpl*)thread->impl; |
|
||||||
pthread_kill(pthreadImpl->thread, SIGUSR1); // thread needs to handle or SIG_IGN this
|
|
||||||
} |
|
||||||
|
|
||||||
void freeThread(Thread** thread) |
|
||||||
{ |
|
||||||
free((ThreadPthreadImpl*)((*thread)->impl)); |
|
||||||
free(*thread); |
|
||||||
*thread = NULL; |
|
||||||
} |
|
||||||
@ -1,158 +0,0 @@ |
|||||||
#include "thread/Watchdog.h" |
|
||||||
#include "thread/Blocker.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <time.h> |
|
||||||
|
|
||||||
#include <pthread.h> |
|
||||||
|
|
||||||
#define TAG "Watchdog" |
|
||||||
|
|
||||||
typedef struct ThreadNode |
|
||||||
{ |
|
||||||
QueuedThread* thread; |
|
||||||
struct ThreadNode* next; |
|
||||||
uint64_t lastCheck; |
|
||||||
} ThreadNode; |
|
||||||
|
|
||||||
static uint64_t getCurrentTimestampMs() { |
|
||||||
uint64_t nowMs; |
|
||||||
struct timespec currentTime; |
|
||||||
clock_gettime(CLOCK_REALTIME, ¤tTime); |
|
||||||
nowMs = (uint64_t)currentTime.tv_sec * 1000UL
|
|
||||||
+ (uint64_t)currentTime.tv_nsec / 1000000UL; |
|
||||||
return nowMs; |
|
||||||
} |
|
||||||
|
|
||||||
static void check(void* arg) |
|
||||||
{ |
|
||||||
ThreadNode* node = (ThreadNode*)arg; |
|
||||||
node->lastCheck = getCurrentTimestampMs(); |
|
||||||
} |
|
||||||
|
|
||||||
static void* watchThread(void* arg) |
|
||||||
{ |
|
||||||
Watchdog* watchdog = (Watchdog*)arg; |
|
||||||
uint64_t sendMs = 0; |
|
||||||
uint64_t responseMs = 0; |
|
||||||
ThreadNode* node = NULL; |
|
||||||
while (watchdog->running) { |
|
||||||
// post check tasks
|
|
||||||
sendMs = getCurrentTimestampMs(); |
|
||||||
node = watchdog->threads; |
|
||||||
while (node != NULL) { |
|
||||||
watchdog->log(TAG, "Posting check task for thread %p", node->thread->thread); |
|
||||||
postQueuedTask(node->thread, &check, node); |
|
||||||
node = node->next; |
|
||||||
} |
|
||||||
|
|
||||||
lockBlockerTimed(watchdog->blocker, watchdog->intervalMs); |
|
||||||
|
|
||||||
node = watchdog->threads; |
|
||||||
while (node != NULL) { |
|
||||||
responseMs = node->lastCheck - sendMs; |
|
||||||
watchdog->log(TAG, "Response time for thread %p = %dms", node->thread->thread, responseMs); |
|
||||||
if (responseMs > watchdog->intervalMs) { |
|
||||||
if (watchdog->eventHandler != NULL && watchdog->running) { |
|
||||||
watchdog->log(TAG, "Thread %p is unresponsive, notifying handler", node->thread->thread); |
|
||||||
watchdog->eventHandler(node->thread->thread); |
|
||||||
} |
|
||||||
} |
|
||||||
node = node->next; |
|
||||||
} |
|
||||||
} |
|
||||||
pthread_exit(NULL); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Watchdog* createWatchdog(uint32_t intervalMs, WatchdogEventHandler eventHandler, LogFn logFn) |
|
||||||
{ |
|
||||||
Watchdog* watchdog = calloc(1, sizeof(Watchdog)); |
|
||||||
watchdog->intervalMs = intervalMs; |
|
||||||
watchdog->threads = NULL; |
|
||||||
watchdog->watchingThread = NULL; |
|
||||||
watchdog->blocker = createBlocker(); |
|
||||||
watchdog->eventHandler = eventHandler; |
|
||||||
watchdog->log = logFn; |
|
||||||
return watchdog; |
|
||||||
} |
|
||||||
|
|
||||||
void startWatchdog(Watchdog* watchdog) |
|
||||||
{ |
|
||||||
if (watchdog->running || watchdog->watchingThread != NULL) { |
|
||||||
printf("Watchdog: Watching thread is already running\n"); |
|
||||||
return; |
|
||||||
} |
|
||||||
watchdog->running = true; |
|
||||||
watchdog->watchingThread = createThread(watchThread, watchdog); |
|
||||||
} |
|
||||||
|
|
||||||
void startWatchdogSync(Watchdog* watchdog) |
|
||||||
{ |
|
||||||
if (watchdog->running) { |
|
||||||
printf("Watchdog: Watching thread is already running\n"); |
|
||||||
return; |
|
||||||
} |
|
||||||
watchdog->running = true; |
|
||||||
watchThread(watchdog); |
|
||||||
} |
|
||||||
|
|
||||||
void stopWatchdog(Watchdog* watchdog) |
|
||||||
{ |
|
||||||
watchdog->running = false; |
|
||||||
notifyBlocker(watchdog->blocker); |
|
||||||
if (watchdog->watchingThread == NULL) { |
|
||||||
return; |
|
||||||
} |
|
||||||
joinThread(watchdog->watchingThread); |
|
||||||
freeThread(&(watchdog->watchingThread)); |
|
||||||
watchdog->watchingThread = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
QueuedThread* createWatchedThread(Watchdog* watchdog) |
|
||||||
{ |
|
||||||
QueuedThread* qthread = createQueuedThread(); |
|
||||||
ThreadNode* node = calloc(1, sizeof(ThreadNode)); |
|
||||||
node->thread = qthread; |
|
||||||
node->next = (watchdog->threads == NULL) ? NULL : watchdog->threads; |
|
||||||
node->lastCheck = getCurrentTimestampMs(); |
|
||||||
watchdog->threads = node; |
|
||||||
return qthread; |
|
||||||
} |
|
||||||
|
|
||||||
void joinWatchedThreads(Watchdog* watchdog) |
|
||||||
{ |
|
||||||
ThreadNode* node = watchdog->threads; |
|
||||||
while (node != NULL) { |
|
||||||
joinQueuedThread(node->thread); |
|
||||||
node = node->next; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void freeWatchedThreads(Watchdog* watchdog) |
|
||||||
{ |
|
||||||
ThreadNode* node = watchdog->threads; |
|
||||||
while (node != NULL) { |
|
||||||
freeQueuedThread(&(node->thread)); |
|
||||||
node = node->next; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void freeWatchdog(Watchdog** watchdog) |
|
||||||
{ |
|
||||||
Watchdog* w = *watchdog; |
|
||||||
ThreadNode* node = w->threads; |
|
||||||
ThreadNode* tmpNode = NULL; |
|
||||||
while (node != NULL) { |
|
||||||
tmpNode = node; |
|
||||||
node = node->next; |
|
||||||
free(tmpNode); |
|
||||||
} |
|
||||||
freeBlocker(&(w->blocker)); |
|
||||||
free(w); |
|
||||||
*watchdog = NULL; |
|
||||||
} |
|
||||||
@ -1,20 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
enable_testing() |
|
||||||
|
|
||||||
set(SRC_DIR ../../src) |
|
||||||
|
|
||||||
include_directories(${SRC_DIR}) |
|
||||||
|
|
||||||
add_executable(ThreadTest Thread.test.c) |
|
||||||
target_link_libraries(ThreadTest thread) |
|
||||||
add_test(ThreadTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ThreadTest) |
|
||||||
|
|
||||||
add_executable(QueuedThreadTest QueuedThread.test.c) |
|
||||||
target_link_libraries(QueuedThreadTest thread) |
|
||||||
add_test(QueuedThreadTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/QueuedThreadTest) |
|
||||||
|
|
||||||
add_executable(WatchdogTest Watchdog.test.c) |
|
||||||
target_link_libraries(WatchdogTest thread) |
|
||||||
add_test(WatchdogTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/WatchdogTest) |
|
||||||
@ -1,36 +0,0 @@ |
|||||||
#include "thread/QueuedThread.h" |
|
||||||
#include "thread/Blocker.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
static const int COUNT = 3; |
|
||||||
static const int SLEEP_US = 200*1000; |
|
||||||
|
|
||||||
|
|
||||||
void task(void* arg); |
|
||||||
|
|
||||||
int main() { |
|
||||||
QueuedThread* thread = createQueuedThread(); |
|
||||||
postQueuedTask(thread, &task, NULL); |
|
||||||
postQueuedTask(thread, &task, NULL); |
|
||||||
|
|
||||||
for (int i = 0; i < COUNT*2+1; i++) { |
|
||||||
printf("Main: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
|
|
||||||
joinQueuedThread(thread); |
|
||||||
freeQueuedThread(&thread); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void task(void* arg) { |
|
||||||
(void)arg; |
|
||||||
for (int i = 0; i < COUNT; i++) { |
|
||||||
printf("Thread: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,47 +0,0 @@ |
|||||||
#include "thread/Thread.h" |
|
||||||
#include "thread/Blocker.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
static const int COUNT = 3; |
|
||||||
static const int SLEEP_US = 200*1000; |
|
||||||
|
|
||||||
|
|
||||||
void* run(void* arg); |
|
||||||
|
|
||||||
int main() { |
|
||||||
Thread* thread = NULL; |
|
||||||
BlockerHandle blocker = createBlocker(); |
|
||||||
if (blocker == -1) { |
|
||||||
printf("Main: Failed to create blocker.\n"); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
thread = createThread(&run, &blocker); |
|
||||||
|
|
||||||
for (int i = 0; i < COUNT+1; i++) { |
|
||||||
printf("Main: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
notifyBlocker(blocker); |
|
||||||
|
|
||||||
joinThread(thread); |
|
||||||
freeThread(&thread); |
|
||||||
freeBlocker(&blocker); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void* run(void* arg) { |
|
||||||
BlockerHandle* blocker = arg; |
|
||||||
for (int i = 0; i < COUNT; i++) { |
|
||||||
printf("Thread: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
printf("Blocker ID: %d\n", *blocker); |
|
||||||
lockBlocker(*blocker); |
|
||||||
printf("Thread: Blocker notified, continuing execution.\n"); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
@ -1,96 +0,0 @@ |
|||||||
#include "thread/Watchdog.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <stdarg.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <assert.h> |
|
||||||
|
|
||||||
static const int COUNT = 3; |
|
||||||
static const int SLEEP_US = 500*1000; |
|
||||||
|
|
||||||
static const int WATCHDOG_INTERVAL_MS = 2000; |
|
||||||
|
|
||||||
static void run1(void* arg); |
|
||||||
static void run2(void* arg); |
|
||||||
static void run3(void* arg); |
|
||||||
|
|
||||||
static QueuedThread* t1 = NULL; |
|
||||||
static QueuedThread* t2 = NULL; |
|
||||||
static QueuedThread* t3 = NULL; |
|
||||||
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0))) |
|
||||||
static void stdLog(const char* tag, const char* format, ...) |
|
||||||
{ |
|
||||||
va_list args; |
|
||||||
printf("%s ", tag); |
|
||||||
va_start(args, format); |
|
||||||
vprintf(format, args); |
|
||||||
va_end(args); |
|
||||||
} |
|
||||||
|
|
||||||
_Noreturn static void onThreadUnresponsive(Thread* thread) |
|
||||||
{ |
|
||||||
printf("Thread %p is unresponsive, exiting\n", (void*)(thread)); |
|
||||||
assert(thread == t3->thread); |
|
||||||
_exit(0); // this actually means passing the test
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{ |
|
||||||
Watchdog* watchdog = createWatchdog(WATCHDOG_INTERVAL_MS, &onThreadUnresponsive, &stdLog); |
|
||||||
|
|
||||||
t1 = createWatchedThread(watchdog); |
|
||||||
t2 = createWatchedThread(watchdog); |
|
||||||
t3 = createWatchedThread(watchdog); |
|
||||||
|
|
||||||
printf("T1: %p\nT2: %p\nT3: %p\n", |
|
||||||
(void*)t1->thread, (void*)t2->thread, (void*)t3->thread); |
|
||||||
|
|
||||||
startWatchdog(watchdog); |
|
||||||
|
|
||||||
postQueuedTask(t1, &run1, NULL); |
|
||||||
postQueuedTask(t2, &run2, NULL); |
|
||||||
postQueuedTask(t3, &run3, NULL); |
|
||||||
|
|
||||||
for (int i = 0; i < COUNT+1; i++) { |
|
||||||
printf("Main: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
|
|
||||||
// give watchdog a chance to spot the hanged thread
|
|
||||||
usleep((WATCHDOG_INTERVAL_MS+500) * 1000); |
|
||||||
|
|
||||||
stopWatchdog(watchdog); |
|
||||||
joinWatchedThreads(watchdog); |
|
||||||
freeWatchedThreads(watchdog); |
|
||||||
freeWatchdog(&watchdog); |
|
||||||
printf("Done.\n"); |
|
||||||
return 1; // this actually means failing the test
|
|
||||||
} |
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void run1(void* arg) { |
|
||||||
(void)arg; |
|
||||||
for (int i = 0; i < COUNT; i++) { |
|
||||||
printf("Thread 1: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void run2(void* arg) { |
|
||||||
(void)arg; |
|
||||||
for (int i = 0; i < COUNT; i++) { |
|
||||||
printf("Thread 2: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
_Noreturn void run3(void* arg) { |
|
||||||
(void)arg; |
|
||||||
for (int i = 0; i < COUNT; i++) { |
|
||||||
printf("Thread 3: %d\n", i); |
|
||||||
usleep(SLEEP_US); |
|
||||||
} |
|
||||||
while (1) {} // loop forever
|
|
||||||
} |
|
||||||
@ -1,17 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
|
||||||
|
|
||||||
set(C_STANDARD 11) |
|
||||||
enable_testing() |
|
||||||
|
|
||||||
set(SRC_DIR ../../src) |
|
||||||
set(INC_DIR ../../include) |
|
||||||
|
|
||||||
include_directories(${SRC_DIR} ${INC_DIR}) |
|
||||||
include_directories(SYSTEM ${EXTERN_DIR}/greatest) |
|
||||||
|
|
||||||
add_executable(TaskQueueTest |
|
||||||
${SRC_DIR}/core/TaskQueue.c |
|
||||||
TaskQueue.test.c |
|
||||||
) |
|
||||||
|
|
||||||
add_test(TaskQueueTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TaskQueueTest) |
|
||||||
@ -1,175 +0,0 @@ |
|||||||
#include "thread/TaskQueue.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdbool.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
// Testing macros:
|
|
||||||
#define RUN_TEST(test_function) \ |
|
||||||
do {test_setup();test_function();test_teardown();} while (0) |
|
||||||
#define CHECK(type, value, operator, expected, message) \ |
|
||||||
do {if (!(value operator expected)) { \
|
|
||||||
printf("-- Failed: %s\n---- Failed expression: %" #type " %s %" #type "\n", \
|
|
||||||
message, value, #operator, expected); ++failCounter;\
|
|
||||||
} else {printf("-- PASSED (%s %s %s)\n", \
|
|
||||||
#value, #operator, #expected);}} while (0) |
|
||||||
static int failCounter = 0; |
|
||||||
|
|
||||||
// usage:
|
|
||||||
// CHECK(type, actual, operator, expected, message);
|
|
||||||
// RUN_TEST(test_function);
|
|
||||||
|
|
||||||
enum { |
|
||||||
IDX_RESULT_TASK_1 = 0, |
|
||||||
IDX_RESULT_TASK_2, |
|
||||||
IDX_RESULT_TASK_ORDER, |
|
||||||
IDX_RESULT_TASK_STOP, |
|
||||||
IDX_RESULT_BLOCK, |
|
||||||
IDX_RESULT_NOTIFY, |
|
||||||
IDX_RESULT_COUNT |
|
||||||
}; |
|
||||||
|
|
||||||
static int taskResults[IDX_RESULT_COUNT] = {0}; |
|
||||||
|
|
||||||
static BlockerHandle blockerHnd = 99; |
|
||||||
static TaskQueuePtr globalQueue = NULL; |
|
||||||
|
|
||||||
void testTask1(void*); |
|
||||||
void testTask2(void*); |
|
||||||
void testTaskStop(void*); |
|
||||||
void testTaskPostItself(void*); |
|
||||||
void blockMock(BlockerHandle); |
|
||||||
void notifyMock(BlockerHandle); |
|
||||||
|
|
||||||
static void test_setup(void){} |
|
||||||
static void test_teardown(void){} |
|
||||||
|
|
||||||
static void test_createTaskQueue(void) |
|
||||||
{ |
|
||||||
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
||||||
CHECK(p, (void*)queue, !=, NULL, "createTaskQueue should return a valid task queue"); |
|
||||||
freeTaskQueue(&queue); |
|
||||||
} |
|
||||||
|
|
||||||
static void test_runQueue(void) |
|
||||||
{ |
|
||||||
const char* msg = "runQueue should execute enqueued tasks in order with no loop"; |
|
||||||
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
||||||
enqueueTask(queue, &testTask1, NULL); |
|
||||||
enqueueTask(queue, &testTask2, NULL); |
|
||||||
enqueueTask(queue, &testTaskStop, queue); |
|
||||||
runQueue(queue); |
|
||||||
|
|
||||||
CHECK(i, taskResults[IDX_RESULT_TASK_1] , ==, 1, msg); |
|
||||||
CHECK(i, taskResults[IDX_RESULT_TASK_2] , ==, 2, msg); |
|
||||||
CHECK(i, taskResults[IDX_RESULT_TASK_ORDER], ==, 3, msg); |
|
||||||
CHECK(i, taskResults[IDX_RESULT_TASK_STOP] , ==, 1, msg); |
|
||||||
|
|
||||||
freeTaskQueue(&queue); |
|
||||||
} |
|
||||||
|
|
||||||
static void test_enqueueNotify(void) |
|
||||||
{ |
|
||||||
const char* msg = "Enqueueing new task should notify the blocker"; |
|
||||||
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
||||||
memset(taskResults, 0, sizeof(taskResults)); |
|
||||||
|
|
||||||
enqueueTask(queue, &testTask1, NULL); |
|
||||||
enqueueTask(queue, &testTask1, NULL); |
|
||||||
|
|
||||||
CHECK(i, taskResults[IDX_RESULT_NOTIFY], ==, 2, msg); |
|
||||||
freeTaskQueue(&queue); |
|
||||||
} |
|
||||||
|
|
||||||
static void test_stopNotify(void) |
|
||||||
{ |
|
||||||
const char* msg = "Stopping the queue should notify the blocker"; |
|
||||||
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
||||||
memset(taskResults, 0, sizeof(taskResults)); |
|
||||||
|
|
||||||
enqueueTask(queue, &testTaskStop, queue); |
|
||||||
runQueue(queue); |
|
||||||
|
|
||||||
CHECK(i, taskResults[IDX_RESULT_NOTIFY], ==, 2, msg); // enqueue + stop
|
|
||||||
|
|
||||||
freeTaskQueue(&queue); |
|
||||||
} |
|
||||||
|
|
||||||
static void test_blockOnEmpty(void) |
|
||||||
{ |
|
||||||
const char* msg = "Empty queue should lock the blocker"; |
|
||||||
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
||||||
memset(taskResults, 0, sizeof(taskResults)); |
|
||||||
|
|
||||||
globalQueue = queue; |
|
||||||
enqueueTask(queue, &testTask1, NULL); |
|
||||||
runQueue(queue); |
|
||||||
|
|
||||||
CHECK(i, taskResults[IDX_RESULT_BLOCK], ==, 1, msg); |
|
||||||
freeTaskQueue(&queue); |
|
||||||
} |
|
||||||
|
|
||||||
static void test_leak(void) |
|
||||||
{ |
|
||||||
// This is a leak test for valgrind.
|
|
||||||
// The scenario is that a task enqueueing itself again
|
|
||||||
// shouldn't cause infinite loop nor memory leak.
|
|
||||||
|
|
||||||
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
||||||
enqueueTask(queue, &testTaskPostItself, queue); |
|
||||||
enqueueTask(queue, &testTaskStop, queue); |
|
||||||
runQueue(queue); |
|
||||||
freeTaskQueue(&queue); |
|
||||||
} |
|
||||||
|
|
||||||
int main(void) { |
|
||||||
RUN_TEST(test_createTaskQueue); |
|
||||||
RUN_TEST(test_runQueue); |
|
||||||
RUN_TEST(test_enqueueNotify); |
|
||||||
RUN_TEST(test_stopNotify); |
|
||||||
RUN_TEST(test_blockOnEmpty); |
|
||||||
RUN_TEST(test_leak); |
|
||||||
return failCounter; |
|
||||||
} |
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void blockMock(BlockerHandle blocker) |
|
||||||
{ |
|
||||||
(void)blocker; |
|
||||||
taskResults[IDX_RESULT_BLOCK] += 1; |
|
||||||
if (globalQueue) { |
|
||||||
stopQueue(globalQueue); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void notifyMock(BlockerHandle blocker) |
|
||||||
{ |
|
||||||
(void)blocker; |
|
||||||
taskResults[IDX_RESULT_NOTIFY] += 1; |
|
||||||
} |
|
||||||
|
|
||||||
void testTask1(void* data)
|
|
||||||
{ |
|
||||||
(void)data; |
|
||||||
taskResults[IDX_RESULT_TASK_1] += 1; |
|
||||||
taskResults[IDX_RESULT_TASK_ORDER] += 6; |
|
||||||
} |
|
||||||
void testTask2(void* data)
|
|
||||||
{ |
|
||||||
(void)data; |
|
||||||
taskResults[IDX_RESULT_TASK_2] += 2; |
|
||||||
taskResults[IDX_RESULT_TASK_ORDER] /= 2; |
|
||||||
} |
|
||||||
|
|
||||||
void testTaskStop(void* data) |
|
||||||
{ |
|
||||||
taskResults[IDX_RESULT_TASK_STOP] += 1; |
|
||||||
stopQueue((TaskQueuePtr)data); |
|
||||||
} |
|
||||||
|
|
||||||
void testTaskPostItself(void* data) |
|
||||||
{ |
|
||||||
enqueueTask((TaskQueuePtr)data, testTaskPostItself, data); |
|
||||||
} |
|
||||||
Loading…
Reference in new issue