Skip to content
Commits on Source (6)
*.o
oss
user
./build
./build/*
*.d
.vscode/settings.json
Morgan Elder
Project 5
CS 4760
The current state of this project does not fulfill the
requirements as stated in the specification. At the moment,
the project compiles two executables oss and user. oss initializes
a process_table and logical_clock in shared memory. Also,
each entry in the process table is associated with a message
queue. Then, oss runs by spawning a user process, sending the
user a message to begin work, and then sends to the user a
message to terminate. When user starts, the process table and
logical clock in shared memory are attached to the process. The
user searches for the message queue in the process table and
waits for a message to begin work. Then, user waits again for
another message from oss. The only option is to receive a
termination message. After receiving the termination message,
user cleans up by detaching memory. Then oss cleans up by
removing shared memory and message queues.
SRC TREE
========
src
├── config.h
├── message_types
│ ├── message_types.c
│ └── message_types.h
├── oss.c
├── oss_helper
│ ├── oss_helper.c
│ └── oss_helper.h
├── process_management
│ ├── communication
│ │ ├── communication.c
│ │ └── communication.h
│ ├── process_table
│ │ ├── process_table.c
│ │ └── process_table.h
│ └── spawn_user
│ ├── spawn_user.c
│ └── spawn_user.h
├── resources
│ ├── logical_clock
│ │ ├── logical_clock.c
│ │ └── logical_clock.h
│ ├── message_queue
│ │ ├── message_queue.c
│ │ └── message_queue.h
│ └── shared_memory
│ ├── shared_memory.c
│ └── shared_memory.h
├── user.c
└── user_helper
├── user_helper.c
└── user_helper.h
COMPILE
=======
In the root of project dir:
> make
This command produces object files and two executables
(oss and user) in the build directory.
EXECUTE
=======
Execute the project from the root directory of the project using:
> make run
or
> ./build/oss
Expected results:
oss_helper: init
oss_helper: run
process_table: add
process_table: get_message_queue_id
user_helper: init
process_table: get_message_queue_id
user_helper: run
user_helper: received end message
user_helper: cleanup
process_table: detach
oss_helper: cleanup
process_table: cleanup
CLEAN
=====
To remove generated files, use the command in the root directory
of the project:
> make clean
......@@ -6,9 +6,10 @@ FIND_SRC = ./scripts/find_src.sh
OSS_TARGET = oss
USER_TARGET = user
OSS_REDQUIRED = oss.c new_process.c shared_memory.c logical_clock.c
USER_REQUIRED = user.c
OSS_REDQUIRED = oss.c oss_helper.c shared_memory.c logical_clock.c message_types.c \
message_queue.c spawn_user.c process_table.c communication.c
USER_REQUIRED = user.c user_helper.c shared_memory.c logical_clock.c message_types.c \
message_queue.c spawn_user.c process_table.c communication.c
OSS_SRCS := $(shell $(FIND_SRC) -d $(SRC_DIRS) -f "$(OSS_REDQUIRED)")
USER_SRCS := $(shell $(FIND_SRC) -d $(SRC_DIRS) -f "$(USER_REQUIRED)")
......@@ -52,6 +53,10 @@ run:
debug:
gdb $(BUILD_DIR)/$(OSS_TARGET)
.PHONY: format
format:
clang-format -i $(shell find $(SRC_DIRS) -name *.c -o -name *.h)
.PHONY: help
help:
@echo "makefile for project 5"
......@@ -59,4 +64,6 @@ help:
@echo "make rebuild - cleans and builds the project"
@echo "make clean - removes the build directory"
@echo "make run - runs the program"
@echo "make debug - runs the program in debug mode"
@echo "make format - formats the code using clang-format"
@echo "make help - displays this message"
#ifndef CONFIG_H
#define CONFIG_H
#define PATH_TO_USER "./build/user"
// Path and id for shared clock
#define CLOCK_PATH "./build/oss"
#define CLOCK_ID 1
#define SHM_CLOCK_ID 0
#define SHM_CLOCK_PATH "./build/oss"
// Path and id for process table
#define PROCESS_TABLE_PATH "./build/oss"
#define PROCESS_TABLE_ID 2
// Path for message queue
#define MESSAGE_QUEUE_PATH "./build/oss"
// user process spawn
#define USER_SPAWN_PATH "./build/user"
// maximum number of user processes
#define MAX_CONCURRENT_USER_PROCESSES 2
#define MAX_TOTAL_USER_PROCESSES 3
#endif
\ No newline at end of file
#include <stdio.h>
#include "logical_clock.h"
#include "shared_memory.h"
/* create_shared_clock
*
* Returns a pointer to a shared logical clock.
* If the clock does not exist, it is created.
* If an error occurs, prints an error message and returns NULL.
*
* path: the path to the file to use for ftok
* id: the id to use for ftok
*
* returns: a pointer to a shared logical clock
*/
logical_clock_t *create_shared_clock(const char *path, int id)
{
static logical_clock_t *clock = NULL;
if (clock == NULL) {
key_t key = get_key(path, id);
if (key == -1) {
return NULL;
}
int shmid = get_shared_memory(key, sizeof(logical_clock_t), CREATE_FLAG);
if (shmid == -1) {
return NULL;
}
clock = attach_shared_memory(shmid);
}
return clock;
}
\ No newline at end of file
#ifndef LOGICAL_CLOCK_H
#define LOGICAL_CLOCK_H
typedef struct logical_clock_s {
unsigned int seconds;
unsigned int nanoseconds;
} logical_clock_t;
logical_clock_t *creat_shared_clock(const char *path, int id);
#endif
\ No newline at end of file
#include "message_types.h"
#include <stdio.h>
#include <string.h>
// external variables
const msg_type_ops_t msg_type_ops = {.get_by_id = get_by_id,
.get_by_type = get_by_type,
.get_by_name = get_by_name};
const message_type_t MESSAGE_TYPES[] = {
{USER_START, 0, "USER_START"},
{USER_END_SUCCESS, 1, "USER_END_SUCCESS"},
};
const int MESSAGE_TYPES_SIZE = 2;
// internal function prototypes
// get the message type by id
static message_type_t *get_by_id(message_type_id_t id) {
if (id < 0 || id >= MESSAGE_TYPES_SIZE) {
fprintf(stderr, "get_by_id: message type not found\n");
return NULL;
}
return (message_type_t *)&MESSAGE_TYPES[id];
}
// get the message type by type
static message_type_t *get_by_type(long type) {
for (int i = 0; i < MESSAGE_TYPES_SIZE; i++) {
if (MESSAGE_TYPES[i].type == type) {
return (message_type_t *)&MESSAGE_TYPES[i];
}
}
fprintf(stderr, "get_by_type: message type not found\n");
return NULL;
}
// get the message type by name
static message_type_t *get_by_name(char *name) {
for (int i = 0; i < MESSAGE_TYPES_SIZE; i++) {
if (strcmp(MESSAGE_TYPES[i].name, name) == 0) {
return (message_type_t *)&MESSAGE_TYPES[i];
}
}
fprintf(stderr, "get_by_name: message type not found\n");
return NULL;
}
\ No newline at end of file
#ifndef MESSAGE_TYPES_H
#define MESSAGE_TYPES_H
// data structures
typedef enum message_type_id_s {
USER_START,
USER_END_SUCCESS,
USER_END_FAILURE,
} message_type_id_t;
typedef struct message_type_s {
message_type_id_t id;
long type;
char *name;
} message_type_t;
// external interface
typedef struct msg_type_ops_s {
message_type_t *(*get_by_id)(message_type_id_t id);
message_type_t *(*get_by_type)(long type);
message_type_t *(*get_by_name)(char *name);
} msg_type_ops_t;
// external variables
extern const msg_type_ops_t msg_type_ops;
extern const message_type_t MESSAGE_TYPES[];
extern const int MESSAGE_TYPES_SIZE;
// internal function prototypes
static message_type_t *get_by_id(message_type_id_t id);
static message_type_t *get_by_type(long type);
static message_type_t *get_by_name(char *name);
#endif
\ No newline at end of file
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "new_process.h"
/*
* This function initializes a new_process_t struct.
*
* Parameters:
* name: name of the process
* argc: number of arguments
* argv: arguments
*
* Returns:
* new_process_t struct
*/
new_process_t *init_new_process(char *name, int argc, char **argv) {
static new_process_t *np = NULL;
if (np == NULL) {
np = (new_process_t *)malloc(sizeof(new_process_t));
if (np == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
}
np->name = name;
np->argc = argc;
np->argv = argv;
return np;
}
/*
* This function runs a new process.
*
* Parameters:
* np: new_process_t struct
*
* Returns:
* 0 if child process
* pid of child process if parent process
* -1 if error
*/
int run_process(new_process_t *np) {
pid_t pid = fork();
if (pid == 0) {
// child process
if (execvp(np->name, np->argv) == -1) {
perror("execvp");
exit(EXIT_FAILURE);
}
return 0;
} else if (pid > 0) {
// parent process
return pid;
} else {
// error
perror("fork");
return -1;
}
}
\ No newline at end of file
#ifndef NEW_PROCESS_H
#define NEW_PROCESS_H
// struct to hold information about creating a new process
typedef struct new_process_s {
char *name; // name of the process
int argc; // number of arguments
char **argv; // arguments
} new_process_t;
new_process_t *init_new_process(char *name, int argc, char **argv);
int run_process(new_process_t *np);
#endif
\ No newline at end of file
#include "oss_helper/oss_helper.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "new_process/new_process.h"
#include "config.h"
#include "shared_memory/shared_memory.h"
#include "logical_clock/logical_clock.h"
// globals
logical_clock_t *clock;
int main() {
oss_helper.init();
// function prototypes
int run_user();
oss_helper.run();
int main(void)
{
printf("Hello, world from oss!\n");
printf("key: %d\n", get_key(SHM_CLOCK_PATH, SHM_CLOCK_ID));
// run_user();
}
int run_user()
{
char *name = PATH_TO_USER;
int argc = 1;
char *argv[] = {name, NULL};
new_process_t *np = init_new_process(name, argc, argv);
return run_process(np);
oss_helper.cleanup();
return 0;
}
\ No newline at end of file
#include "oss_helper.h"
#include "../config.h"
#include "../message_types/message_types.h"
#include "../process_management/communication/communication.h"
#include "../process_management/process_table/process_table.h"
#include "../process_management/spawn_user/spawn_user.h"
#include "../resources/logical_clock/logical_clock.h"
#include "../resources/message_queue/message_queue.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
// external variables
const oss_helper_t oss_helper = {
.init = oss_init, .run = oss_run, .cleanup = oss_cleanup};
// internal functions
// initializes oss data structures
// called when oss process starts
static void oss_init() {
printf("oss_helper: init\n");
const int STEPS = 4;
int valid_step = 1;
int i;
int step;
for (step = 0; step < STEPS && valid_step; step++) {
switch (step) {
case 0:
// register signal handler
signal(SIGINT, oss_sig_handler);
signal(SIGSEGV, oss_sig_handler);
break;
case 1:
// initialize logical clock
clock = logical_clock_ops.create_shm(CLOCK_PATH, CLOCK_ID);
if (clock == NULL) {
valid_step = 0;
}
break;
case 2:
// initialize process table
process_table = pt_ops.create(PROCESS_TABLE_PATH, PROCESS_TABLE_ID);
if (process_table == NULL) {
valid_step = 0;
}
// create message queue for each user process
for (i = 0; i < process_table->capacity; i++) {
process_table->entries[i].message_queue_id =
msg_ops.open(MESSAGE_QUEUE_PATH, i, OPEN_CREATE);
if (process_table->entries[i].message_queue_id == -1) {
valid_step = 0;
break;
}
}
break;
}
}
if (!valid_step) {
fprintf(stderr, "oss_helper: init failed\n");
oss_cleanup();
exit(EXIT_FAILURE);
}
}
// runs oss
// called after oss data structures are initialized
static void oss_run() {
printf("oss_helper: run\n");
// spawn user and add to process table
int user_pid = spawn_ops.spawn(USER_SPAWN_PATH);
if (user_pid == -1) {
fprintf(stderr, "oss_helper: failed to spawn user\n");
oss_cleanup();
exit(EXIT_FAILURE);
}
pt_ops.add(process_table, user_pid);
// create message queue using user_pid as msgflg
int message_queue_id = pt_ops.get_message_queue_id(process_table, user_pid);
spawn_ops.send_start_msg(message_queue_id, user_pid);
// send terminate message to user
message_type_t *type = msg_type_ops.get_by_id(USER_END_SUCCESS);
comms.send(message_queue_id, type->name, type->type);
waitpid(user_pid, NULL, 0);
}
// cleans up oss data structures
// called when oss process terminates
static void oss_cleanup() {
printf("oss_helper: cleanup\n");
if (clock != NULL) {
logical_clock_ops.detach_shm(clock);
logical_clock_ops.destroy_shm(CLOCK_PATH, CLOCK_ID);
}
if (process_table != NULL) {
pt_ops.cleanup(process_table, PROCESS_TABLE_PATH, PROCESS_TABLE_ID);
}
}
// signal handler
// called when signal is sent to oss process
static void oss_sig_handler(int sig) {
switch (sig) {
case SIGINT:
printf("oss: SIGINT\n");
break;
case SIGSEGV:
printf("oss: SIGSEGV\n");
break;
default:
printf("oss: signal %d\n", sig);
}
oss_cleanup();
exit(EXIT_SUCCESS);
}
\ No newline at end of file
#ifndef OSS_HELPER_H
#define OSS_HELPER_H
// This files contains the helper functions for oss
// includes
#include "../config.h"
#include "../process_management/process_table/process_table.h"
#include "../resources/logical_clock/logical_clock.h"
// external interface
typedef struct oss_helper_s {
void (*init)(void);
void (*run)(void);
void (*cleanup)(void);
} oss_helper_t;
// external variables
extern const oss_helper_t oss_helper;
// internal variables
static logical_clock_t *clock;
static process_table_t *process_table;
// internal functions prototypes
static void oss_init(void);
static void oss_run(void);
static void oss_cleanup(void);
static void oss_sig_handler(int sig);
#endif
\ No newline at end of file
#include "communication.h"
#include "../../message_types/message_types.h"
#include "../../resources/message_queue/message_queue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const comms_t comms = {
.send = comms_send,
.check = comms_check,
};
// internal function prototypes
// send a message
// returns 0 on success, -1 on failure
static int comms_send(int queue_id, char *data, message_type_id_t type_id) {
msg_t *msg;
message_type_t *type;
// get the message type
if ((type = msg_type_ops.get_by_id(type_id)) == NULL) {
fprintf(stderr, "comms_send: invalid message type id\n");
return -1;
}
// create the message
if ((msg = msg_ops.create(type->type, data)) == NULL) {
fprintf(stderr, "comms_send: msg_ops.create failed\n");
return -1;
}
// send the message
if (msg_ops.send(queue_id, msg) == -1) {
fprintf(stderr, "comms_send: msg_ops.send failed\n");
return -1;
}
// free the message
free(msg);
return 0;
}
// check for a message using a non-blocking receive
// modifies msg parameter to point to the message if found
// returns 0 if a message was found, -1 on error,
// 1 if no message was found
static int comms_check(int queue_id, msg_t *msg) {
message_type_t *type;
// create a message
if ((msg = msg_ops.create(0, "")) == NULL) {
fprintf(stderr, "comms_check: msg_ops.create failed\n");
return -1;
}
// check for a message of any type using a non-blocking receive
int type_id;
int found = 1;
for (type_id = 0; type_id < MESSAGE_TYPES_SIZE; type_id++) {
type = msg_type_ops.get_by_id(type_id);
found = msg_ops.recv_nonblock(queue_id, msg, type->type);
if (found == 0) {
// found a message
break;
} else if (found == -1) {
// error
fprintf(stderr, "comms_check: msg_ops.recv_nonblock failed\n");
return -1;
}
}
if (found == 0) {
// found a message
return 0;
} else {
// no message found
free(msg);
return 1;
}
}
#ifndef COMMUNICATION_H
#define COMMUNICATION_H
// includes
#include "../../message_types/message_types.h"
#include "../../resources/message_queue/message_queue.h"
typedef struct comms_s {
int (*send)(int queue_id, char *data, message_type_id_t type_id);
int (*check)(int queue_id, msg_t *msg);
} comms_t;
extern const comms_t comms;
// internal function prototypes
static int comms_send(int queue_id, char *data, message_type_id_t type_id);
static int comms_check(int queue_id, msg_t *msg);
#endif
\ No newline at end of file
#include "process_table.h"
#include "../resources/message_queue/message_queue.h"
#include "../resources/shared_memory/shared_memory.h"
#include <stdio.h>
// external variables
const pt_ops_t pt_ops = {.create = pt_create,
.get = pt_get,
.add = pt_add,
.remove = pt_remove,
.get_message_queue_id = pt_get_message_queue_id,
.detach = pt_detach,
.cleanup = pt_cleanup};
// internal variables
static const int CAPACITY = MAX_CONCURRENT_USER_PROCESSES;
// process table operations
// creates process table with given capacity in shared memory
// returns pointer to process table on success, NULL on failure
static process_table_t *pt_create(const char *path, int proj_id) {
static process_table_t *table = NULL;
if (table == NULL) {
table = shm_ops.attach(shm_ops.create(shm_ops.gen_key(path, proj_id),
sizeof(process_table_t)));
if (table != NULL) {
pt_init(table);
}
}
return table;
}
static process_table_t *pt_get(const char *path, int proj_id) {
static process_table_t *table = NULL;
if (table == NULL) {
table = shm_ops.attach(
shm_ops.get(shm_ops.gen_key(path, proj_id), sizeof(process_table_t)));
}
return table;
}
// initializes process table
static void pt_init(process_table_t *table) {
table->size = 0;
table->capacity = CAPACITY;
int i;
for (i = 0; i < table->capacity; i++) {
table->entries[i].pid = -1;
table->entries[i].message_queue_id = -1;
table->entries[i].message_from_user = 0;
table->entries[i].message_from_oss = 0;
table->entries[i].is_active = 0;
}
}
// adds process to process table
static void pt_add(process_table_t *table, int pid) {
printf("process_table: add\n");
int i;
for (i = 0; i < table->capacity; i++) {
if (table->entries[i].pid == -1) {
table->entries[i].pid = pid;
table->entries[i].message_from_user = 0;
table->entries[i].message_from_oss = 0;
table->entries[i].is_active = 1;
break;
}
}
}
// removes process from process table
static void pt_remove(process_table_t *table, int pid) {
printf("process_table: remove\n");
int i;
for (i = 0; i < table->capacity; i++) {
if (table->entries[i].pid == pid) {
table->entries[i].pid = -1;
table->entries[i].message_from_user = 0;
table->entries[i].message_from_oss = 0;
table->entries[i].is_active = 0;
break;
}
}
}
// returns message queue id for given pid
// returns -1 if pid not found
static int pt_get_message_queue_id(process_table_t *table, int pid) {
printf("process_table: get_message_queue_id\n");
int queue_id = -1;
int i;
for (i = 0; i < table->capacity; i++) {
if (table->entries[i].pid == pid) {
queue_id = table->entries[i].message_queue_id;
break;
}
}
return queue_id;
}
// detaches from shared memory
static void pt_detach(process_table_t *table) {
printf("process_table: detach\n");
shm_ops.detach(table);
}
// clean up process table
static void pt_cleanup(process_table_t *table, const char *path, int proj_id) {
printf("process_table: cleanup\n");
// close message queues if they exist
int i;
for (i = 0; i < table->capacity; i++) {
if (table->entries[i].message_queue_id != -1) {
msg_ops.close(table->entries[i].message_queue_id);
}
}
// detach from shared memory
shm_ops.detach(table);
// remove shared memory
shm_ops.destroy(
shm_ops.get(shm_ops.gen_key(path, proj_id), sizeof(process_table_t)));
}
\ No newline at end of file
#ifndef PROCESS_TABLE_H
#define PROCESS_TABLE_H
// includes
#include "../../config.h"
// data structures
typedef struct process_entry_s {
int pid;
int message_queue_id;
int message_from_user;
int message_from_oss;
int is_active;
} process_entry_t;
typedef struct process_table_s {
process_entry_t entries[MAX_CONCURRENT_USER_PROCESSES];
int size;
int capacity;
} process_table_t;
// external interface
typedef struct pt_ops_s {
process_table_t *(*create)(const char *path, int proj_id);
process_table_t *(*get)(const char *path, int proj_id);
void (*add)(process_table_t *table, int pid);
void (*remove)(process_table_t *table, int pid);
int (*get_message_queue_id)(process_table_t *table, int pid);
void (*detach)(process_table_t *table);
void (*cleanup)(process_table_t *table, const char *path, int proj_id);
} pt_ops_t;
// external variables
extern const pt_ops_t pt_ops;
// internal variables
static const int CAPACITY;
// internal function prototypes
static process_table_t *pt_create(const char *path, int proj_id);
static process_table_t *pt_get(const char *path, int proj_id);
static void pt_init(process_table_t *table);
static void pt_add(process_table_t *table, int pid);
static void pt_remove(process_table_t *table, int pid);
static int pt_get_message_queue_id(process_table_t *table, int pid);
static void pt_detach(process_table_t *table);
static void pt_cleanup(process_table_t *table, const char *path, int proj_id);
#endif
\ No newline at end of file
#include "process_management/spawn_user/spawn_user.h"
#include "message_types/message_types.h"
#include "resources/message_queue/message_queue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
// external interface
const spawn_ops_t spawn_ops = {
.spawn = spawn,
.send_start_msg = send_start_msg,
.recv_start_msg = recv_start_msg,
};
// spawn a user process
// returns the pid of the spawned process on success, -1 on failure
int spawn(const char *name) {
pid_t pid;
if ((pid = fork()) == -1) {
perror("spawn: fork");
return -1;
}
if (pid == 0) {
// child process
if (execl(name, name, NULL) == -1) {
perror("spawn: execl");
return -1;
}
}
return pid;
}
// send a start message
// returns 0 on success, -1 on failure
int send_start_msg(int msg_id, int pid) {
msg_t *msg;
message_type_t *msg_type;
// get the message type
if ((msg_type = msg_type_ops.get_by_id(USER_START)) == NULL) {
fprintf(stderr, "send_start_msg: msg_type_ops.get_by_id failed\n");
return -1;
}
// create the message
if ((msg = msg_ops.create(msg_type->type, msg_type->name)) == NULL) {
fprintf(stderr, "send_start_msg: msg_ops.create failed\n");
return -1;
}
// send the message
if (msg_ops.send(msg_id, msg) == -1) {
fprintf(stderr, "send_start_msg: msg_ops.send failed\n");
return -1;
}
// free the message
free(msg);
return 0;
}
// receive a start message
// returns a pointer to the message on success, NULL on failure
msg_t *recv_start_msg(int msg_id, int pid) {
static msg_t *msg = NULL;
message_type_t *msg_type = msg_type_ops.get_by_id(USER_START);
if (msg_type == NULL) {
fprintf(stderr, "recv_start_msg: get_by_id failed\n");
return NULL;
}
// get the message
msg = msg_ops.create(msg_type->type, msg_type->name);
if (msg == NULL) {
fprintf(stderr, "recv_start_msg: msg_ops.create failed\n");
return NULL;
}
// receive the message
if (msg_ops.recv(msg_id, msg, msg_type->type) == -1) {
fprintf(stderr, "recv_start_msg: msg_ops.recv failed\n");
return NULL;
}
return msg;
}
#ifndef SPAWN_USER_H
#define SPAWN_USER_H
// includes
#include "resources/message_queue/message_queue.h"
// data structures
typedef struct spawn_ops_s {
int (*spawn)(const char *name);
int (*send_start_msg)(int msg_id, int pid);
msg_t *(*recv_start_msg)(int msg_id, int pid);
} spawn_ops_t;
// external interface
extern const spawn_ops_t spawn_ops;
// internal function prototypes
int spawn(const char *name);
int send_start_msg(int msg_id, int pid);
msg_t *recv_start_msg(int msg_id, int pid);
#endif
\ No newline at end of file
#include "logical_clock.h"
#include "shared_memory/shared_memory.h"
#include <stdio.h>
// external variables
const logical_clock_ops_t logical_clock_ops = {
.init = logical_clock_init,
.increment = logical_clock_increment,
.compare = logical_clock_compare,
.print = logical_clock_print,
.create_shm = logical_clock_create_shm,
.get_shm = logical_clock_get_shm,
.detach_shm = logical_clock_detach_shm,
.destroy_shm = logical_clock_destroy_shm};
// internal variables
// internal function prototypes
// logical clock operations
// initialize logical clock
static void logical_clock_init(logical_clock_t *clock) {
clock->seconds = 0;
clock->nanoseconds = 0;
}
// increment logical clock
static void logical_clock_increment(logical_clock_t *clock,
unsigned int seconds,
unsigned int nanoseconds) {
clock->nanoseconds += nanoseconds;
if (clock->nanoseconds >= 1000000000) {
clock->seconds += clock->nanoseconds / 1000000000;
clock->nanoseconds %= 1000000000;
}
}
// compare two logical clocks
// return -1 if clock1 < clock2
// return 0 if clock1 == clock2
// return 1 if clock1 > clock2
static int logical_clock_compare(logical_clock_t *clock1,
logical_clock_t *clock2) {
if (clock1->seconds < clock2->seconds) {
return -1;
} else if (clock1->seconds > clock2->seconds) {
return 1;
} else {
if (clock1->nanoseconds < clock2->nanoseconds) {
return -1;
} else if (clock1->nanoseconds > clock2->nanoseconds) {
return 1;
} else {
return 0;
}
}
}
// print logical clock
static void logical_clock_print(logical_clock_t *clock) {
printf("%u.%09u", clock->seconds, clock->nanoseconds);
}
// create shared memory for logical clock
// if shm ops fails, return NULL
static logical_clock_t *logical_clock_create_shm(const char *path, int id) {
static logical_clock_t *clock = NULL;
if (clock == NULL) {
clock = shm_ops.attach(
shm_ops.create(shm_ops.gen_key(path, id), sizeof(logical_clock_t)));
}
return clock;
}
// get shared memory for logical clock
// if shm ops fails, return NULL
static logical_clock_t *logical_clock_get_shm(const char *path, int id) {
static logical_clock_t *clock = NULL;
if (clock == NULL) {
clock = shm_ops.attach(
shm_ops.get(shm_ops.gen_key(path, id), sizeof(logical_clock_t)));
}
return clock;
}
// detach shared memory for logical clock
static void logical_clock_detach_shm(logical_clock_t *clock) {
shm_ops.detach(clock);
}
// destroy shared memory for logical clock
static void logical_clock_destroy_shm(const char *path, int id) {
shm_ops.destroy(
shm_ops.get(shm_ops.gen_key(path, id), sizeof(logical_clock_t)));
}
\ No newline at end of file