Created
March 23, 2025 12:58
-
-
Save amyinorbit/dbe1acb3e5ca984c3f82635c4b7935a4 to your computer and use it in GitHub Desktop.
Thread-safe data access in X-Plane
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*===--------------------------------------------------------------------------------------------=== | |
* xpdata.c | |
* | |
* Created by Amy Parent <[email protected]> | |
* Copyright (c) 2025 Amy Parent. All rights reserved | |
* | |
* Licensed under the MIT License | |
*===--------------------------------------------------------------------------------------------=== | |
*/ | |
#include "xpdata.h" | |
#include <acfutils/assert.h> | |
#include <acfutils/dr.h> | |
#include <acfutils/thread.h> | |
// This is data that stays in this file and never gets out | |
// (that's what the static keyword enforces) | |
// Each bit of data we have is protected by a mutex. Mutexes are a way to ensure that | |
// only one thread is doing things with a piece of data at a given time. | |
static mutex_t data_mutex; | |
static xp_ahrs_t data; | |
// And this is just a way to get all our dataref objects in the same place, you don't really have | |
// to do this, that's just how I like to organise my code | |
static struct { | |
dr_t ahrs_pitch; | |
dr_t ahrs_roll; | |
dr_t ahrs_heading; | |
dr_t adc_ias; | |
dr_t adc_tas; | |
//... | |
} dr; | |
static bool is_init = false; | |
void xp_data_init() { | |
ASSERT(!is_init); | |
// Initialise our mutex. | |
mutex_init(&data_mutex); | |
// Make sure our saved data is all a known value (0) | |
BZERO(&data); | |
// Fetch all our datarefs! | |
fdr_find(&dr.ahrs_pitch, "..."); | |
fdr_find(&dr.ahrs_roll, "..."); | |
fdr_find(&dr.ahrs_heading, "..."); | |
fdr_find(&dr.adc_ias, "..."); | |
fdr_find(&dr.adc_tas, "..."); | |
// ... | |
// And mark ourselves as initialised. | |
is_init = true; | |
} | |
void xp_data_fini() { | |
ASSERT(is_init); | |
// Clean up our mutex. | |
mutex_destroy(&data_mutex); | |
// Mark ourselves as not initialised. | |
is_init = false; | |
} | |
void xp_data_update() { | |
ASSERT(is_init); | |
// By "entering" the mutex, we lock it: that means that any other thread that tries to | |
// enter the mutex will pause until we exit it. | |
mutex_enter(&data_mutex); | |
// Now that we know no-one else is touching `data`, we can fetch the values from each | |
// dataref and store it in there | |
data.ahrs_pitch = dr_getf(&dr.ahrs_pitch); | |
data.ahrs_roll = dr_getf(&dr.ahrs_roll); | |
data.ahrs_heading = dr_getf(&dr.ahrs_heading); | |
data.adc_ias = dr_getf(&dr.adc_ias); | |
data.adc_tas = dr_getf(&dr.adc_tas); | |
// When we're done modifying fields in `data`, we can "exit"/release the mutex | |
mutex_exit(&data_mutex); | |
} | |
void xp_data_get(xp_data_t *out) { | |
// Since we are about to read from our own copy of data, we need to first enter the mutex! | |
mutex_enter(&data_mutex); | |
// Then we can copy our own version of data to `out` | |
*out = data; | |
// And release the mutex when we're done so other threads can do things here. | |
mutex_exit(&data_mutex); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*===--------------------------------------------------------------------------------------------=== | |
* xpdata.h | |
* | |
* Created by Amy Parent <[email protected]> | |
* Copyright (c) 2025 Amy Parent | |
* | |
* Licensed under the MIT License | |
*===--------------------------------------------------------------------------------------------=== | |
*/ | |
#ifndef _XPDATA_H_ | |
#define _XPDATA_H_ | |
#include <stdbool.h> | |
typedef struct { | |
float ahrs_pitch; | |
float ahrs_roll; | |
float ahrs_heading; | |
float adc_ias; | |
float adc_tas; | |
// ... | |
} xp_data_t; | |
// This sets up all the datarefs and all. Call this early, probably in XPluginEnable | |
void xp_data_init(); | |
// This tears down any data we had to set up. Call this in XPluginDisable. | |
void xp_data_fini(); | |
// This **must** be called on the X-Plane thread. You probably want to call it every frame | |
// in a flight loop callback. | |
void xp_data_update(); | |
// This can be called on any thread, and gives you the data that was obtained from datarefs in | |
// xp_data_update. The way to call them is: | |
// | |
// xp_data_t data = {}; | |
// xp_data_get_data(&data); | |
// // use the data here! | |
// | |
void xp_data_get(xp_data_t *data); | |
#endif /* ifndef _XPDATA_H_ */ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment