summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--user/forkman.c114
1 files changed, 82 insertions, 32 deletions
diff --git a/user/forkman.c b/user/forkman.c
index 32dbeb8..538992b 100644
--- a/user/forkman.c
+++ b/user/forkman.c
@@ -49,13 +49,28 @@ typedef struct {
enum tile_type type;
} tile;
+#define CLIENT_BIT (1)
+#define SERVER_BIT (2)
+#define CLIENT_WAITING_BIT (1)
+#define SERVER_WAITING_BIT (2)
+
+typedef struct {
+ volatile uint8_t inner;
+} spinlock;
+
+enum key_state {
+ KEY_STATE_PRESSED,
+ KEY_STATE_UNPRESSED,
+ // TODO: JUST_PRESSED and JUST_RELEASED
+};
+
typedef struct {
volatile size_t frame;
volatile size_t dummy_counter;
- volatile int lock;
volatile uint8_t client_barrier;
volatile uint8_t server_barrier;
- volatile uint8_t key_status[255];
+ volatile enum key_state key_status[255];
+ spinlock player_lock;
volatile vec player_pos;
volatile vec player_vel;
volatile tile tiles[GAME_HEIGHT_TILES * GAME_WIDTH_TILES];
@@ -66,7 +81,9 @@ static int display_server_entry(sharedmem *);
static int client_entry(sharedmem *);
static void barrier_wait(sharedmem *, int isclient);
static volatile tile *tile_at(sharedmem *, size_t x, size_t y);
-static long abs(long);
+static int is_inbounds(size_t x, size_t y);
+static void spinlock_lock(volatile spinlock *lock, int isclient);
+static void spinlock_unlock(volatile spinlock *lock, int isclient);
int main(void)
{
@@ -176,13 +193,6 @@ static void init_level(sharedmem *shared)
shared->player_pos = (vec){ .x = 5 * TILE_WIDTH, .y = 5 * TILE_HEIGHT };
}
-static long abs(long i)
-{
- if (i < 0)
- return i * -1;
- return i;
-}
-
static size_t get_total_time(size_t tick_start) // arbitrary units
{
// 60 is arbitrary pretend fps
@@ -199,30 +209,25 @@ static int display_server_entry(sharedmem *shared)
fb.size = (fb.width * fb.height * fb.bpp) / 8;
- size_t last_frame = 1;
-
barrier_wait(shared, 0);
while (1) {
- if (shared->frame == last_frame)
- continue;
-
struct keycode keycode;
if (keypoll(&keycode)) {
if (keycode.flags & KC_FLAG_KEY_DOWN) {
- shared->key_status[(uint8_t)keycode.key] = 1;
+ shared->key_status[(uint8_t)keycode.key] = KEY_STATE_PRESSED;
}
if (keycode.flags & KC_FLAG_KEY_UP) {
- shared->key_status[(uint8_t)keycode.key] = 0;
+ shared->key_status[(uint8_t)keycode.key] = KEY_STATE_UNPRESSED;
}
}
draw_tiles(shared, &fb);
- draw_player(&fb, shared->player_pos);
- last_frame += 1;
- shared->frame = last_frame;
+ spinlock_lock(&shared->player_lock, 0);
+ draw_player(&fb, shared->player_pos);
+ spinlock_unlock(&shared->player_lock, 0);
}
return 0;
@@ -232,19 +237,15 @@ static int client_entry(sharedmem *shared)
{
init_level(shared);
- size_t last_frame = shared->frame;
-
size_t start_ticks = ticks();
double last_time = get_total_time(start_ticks);
barrier_wait(shared, 1);
do {
- if (last_frame == shared->frame)
- continue;
-
double time = get_total_time(start_ticks);
double delta_time = time - last_time;
+ spinlock_lock(&shared->player_lock, 1);
shared->player_vel.y -= 9.8 * delta_time;
@@ -259,21 +260,21 @@ static int client_entry(sharedmem *shared)
for (size_t i = 0; i < shared->player_vel.x; ++i) {
size_t x = shared->player_pos.x + 1;
size_t y = shared->player_pos.y + 1;
- if (tile_at(shared, x, y)->type != TILE_AIR) {
+ // boundscheck to consider outside tiles to be solid
+ if (is_inbounds(x, y) && tile_at(shared, x, y)->type != TILE_AIR) {
break;
}
shared->player_pos.x += 1;
shared->player_pos.y += 1;
}
- if (shared->key_status[KEY_SPACE]) {
+ if (shared->key_status[KEY_SPACE] == KEY_STATE_PRESSED) {
shared->player_vel.y = 10;
- } else if (shared->key_status[KEY_B]) {
+ } else if (shared->key_status[KEY_B] == KEY_STATE_PRESSED) {
shared->player_vel.y = -10;
}
+ spinlock_unlock(&shared->player_lock, 1);
- last_frame += 1;
- shared->frame = last_frame;
} while (1);
return 0;
@@ -281,16 +282,65 @@ static int client_entry(sharedmem *shared)
static volatile tile *tile_at(sharedmem *shared, size_t x, size_t y)
{
- const size_t idx = x + (y * GAME_WIDTH_TILES);
#ifdef DBG
- if (idx > GAME_WIDTH_TILES * GAME_HEIGHT_TILES) {
+ if (!is_inbounds(x, y)) {
printf("out of bounds");
exit(0);
}
#endif
+ const size_t idx = x + (y * GAME_WIDTH_TILES);
return shared->tiles + idx;
}
+static int is_inbounds(size_t x, size_t y)
+{
+ const size_t idx = x + (y * GAME_WIDTH_TILES);
+ return idx < (GAME_WIDTH_TILES * GAME_HEIGHT_TILES);
+}
+
+static void spinlock_lock(volatile spinlock *lock, int isclient)
+{
+ const uint8_t bit = isclient ? CLIENT_BIT : SERVER_BIT;
+ const uint8_t otherbit = isclient ? SERVER_BIT : CLIENT_BIT;
+
+ // wait for us to be the only waiter
+ while (1) {
+ if (lock->inner == 0) {
+ lock->inner |= bit;
+ // recover from the possibility that something happened between that
+ // if statement and the |= operation. check for other bits being set
+ if ((lock->inner ^ bit) != 0) {
+ // okay, somebody messed with something, undo what we did and
+ // keep waiting
+ lock->inner ^= bit;
+ continue;
+ }
+
+ // okay all good, we are the exclusive owner
+ break;
+ }
+ }
+
+#ifdef DBG
+ // when we own the lock, only our bit should be active and the other proc's
+ // bit should not be active
+ if (lock->inner & otherbit || !(lock->inner & bit)) {
+ printf("SPINLOCK BAD\n");
+ exit(1);
+ }
+#endif
+}
+
+static void spinlock_unlock(volatile spinlock *lock, int isclient)
+{
+ const uint8_t bit = isclient ? CLIENT_BIT : SERVER_BIT;
+
+ // assume our thread of execution (process) is the only one changing our
+ // bit. this can be screwed over if the other process lies about whether
+ // it is server or client
+ lock->inner ^= bit;
+}
+
static void barrier_wait(sharedmem *shared, int isclient)
{
if (isclient) {