linux file track stuff working afaict

master
insofaras 2016-08-30 02:45:36 +01:00
parent 0378546dd8
commit a0f3996e73
3 changed files with 293 additions and 417 deletions

View File

@ -1,4 +1,5 @@
CPP_FILES := $(wildcard *.cpp) $(wildcard **/*.cpp) CPP_FILES := $(wildcard *.cpp) $(wildcard **/*.cpp)
C_FILES := $(wildcard *.c) $(wildcard **/*.c)
H_FILES := $(wildcard *.h) $(wildcard **/*.h) H_FILES := $(wildcard *.h) $(wildcard **/*.h)
WARNINGS := -Wno-write-strings WARNINGS := -Wno-write-strings
PLAT_LINKS := -L/usr/local/lib -lX11 -lpthread -lm -lrt -lGL -ldl -lXfixes -lfreetype -lfontconfig PLAT_LINKS := -L/usr/local/lib -lX11 -lpthread -lm -lrt -lGL -ldl -lXfixes -lfreetype -lfontconfig
@ -9,10 +10,10 @@ FLAGS := -fPIC -fno-threadsafe-statics -pthread -I../foreign $(shell pkg-co
debug: FLAGS += -DFRED_INTERNAL=1 -DFRED_SUPER=1 -g -O0 debug: FLAGS += -DFRED_INTERNAL=1 -DFRED_SUPER=1 -g -O0
debug: ../4ed_app.so ../4ed debug: ../4ed_app.so ../4ed
../4ed_app.so: $(CPP_FILES) $(H_FILES) ../4ed_app.so: $(CPP_FILES) $(C_FILES) $(H_FILES)
g++ $(WARNINGS) $(FLAGS) -shared 4ed_app_target.cpp -iquoteforeign -o $@ g++ $(WARNINGS) $(FLAGS) -shared 4ed_app_target.cpp -iquoteforeign -o $@
../4ed: $(CPP_FILES) $(H_FILES) ../4ed: $(CPP_FILES) $(C_FILES) $(H_FILES)
g++ $(WARNINGS) $(FLAGS) linux_4ed.cpp -iquoteforeign $(PLAT_LINKS) -o $@ g++ $(WARNINGS) $(FLAGS) linux_4ed.cpp -iquoteforeign $(PLAT_LINKS) -o $@
clean: clean:

View File

@ -1,10 +1,10 @@
/* /*
The OS agnostic file tracking API for applications The OS agnostic file tracking API for applications
that want to interact with potentially many files on that want to interact with potentially many files on
the disk that could be changed by other applications. the disk that could be changed by other applications.
Created on: 29.08.2016 Created on: 29.08.2016
*/ */
@ -43,8 +43,16 @@ typedef struct {
// Right now the way the API works, it assumes that the user will // Right now the way the API works, it assumes that the user will
// only ever add memory and never free it. If freeing becomes necessary // only ever add memory and never free it. If freeing becomes necessary
// on Linux the API can be extended to support that. // on Linux the API can be extended to support that.
int inotify;
pthread_mutex_t lock;
char *string_mem_begin;
char *string_mem_end;
} Linux_File_Track_Vars; } Linux_File_Track_Vars;
//static_assert(sizeof(Linux_File_Track_Vars) <= sizeof(File_Track_System));
typedef struct { typedef struct {
File_Index hash; File_Index hash;
// NOTE(allen): // NOTE(allen):
@ -54,8 +62,11 @@ typedef struct {
// would be fine. If it is used hash should be the first element // would be fine. If it is used hash should be the first element
// of this struct and it should be used to uniquely identify each // of this struct and it should be used to uniquely identify each
// entry because it is used as the key for the table. // entry because it is used as the key for the table.
char* filename;
} Linux_File_Track_Entry; } Linux_File_Track_Entry;
//static_assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry));
#define to_vars(s) ((Linux_File_Track_Vars*)(s)) #define to_vars(s) ((Linux_File_Track_Vars*)(s))
#define to_tables(v) ((File_Track_Tables*)(v->tables)) #define to_tables(v) ((File_Track_Tables*)(v->tables))
@ -65,24 +76,29 @@ init_track_system(File_Track_System *system,
void *listener_memory, int32_t listener_memory_size){ void *listener_memory, int32_t listener_memory_size){
File_Track_Result result = FileTrack_MemoryTooSmall; File_Track_Result result = FileTrack_MemoryTooSmall;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
Assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry)); Assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry));
if (enough_memory_to_init_table(table_memory_size) && if (enough_memory_to_init_table(table_memory_size) &&
/*if listener memory is important check it's size here*/ 1){ /*if listener memory is important check it's size here*/ 1){
// NOTE(allen): Initialize main data tables // NOTE(allen): Initialize main data tables
vars->tables = table_memory; vars->tables = (File_Track_Tables*) table_memory;
File_Track_Tables *tables = to_tables(vars); File_Track_Tables *tables = to_tables(vars);
init_table_memory(tables, table_memory_size); init_table_memory(tables, table_memory_size);
// NOTE(allen): vars->inotify = inotify_init1(IN_NONBLOCK);
// Here prepare the data structure and synchronization primatives
// in the vars struct. pthread_mutex_init(&vars->lock, NULL);
vars->string_mem_begin = (char*)listener_memory;
vars->string_mem_end = (char*)listener_memory + listener_memory_size;
result = FileTrack_Good; result = FileTrack_Good;
} }
LINUX_FN_DEBUG("result: %d", result);
return(result); return(result);
} }
@ -90,9 +106,10 @@ FILE_TRACK_LINK File_Track_Result
add_listener(File_Track_System *system, char *filename){ add_listener(File_Track_System *system, char *filename){
File_Track_Result result = FileTrack_Good; File_Track_Result result = FileTrack_Good;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
File_Track_Tables *tables = to_tables(vars);
// ACQUIRE LOCK (only keep this if either of the data structures is kept)
pthread_mutex_lock(&vars->lock);
// NOTE(allen): // NOTE(allen):
// Here do something to begin listening to changes to the file named filename. // Here do something to begin listening to changes to the file named filename.
// On Windows it listens to the parent directory if no other file in that // On Windows it listens to the parent directory if no other file in that
@ -101,9 +118,43 @@ add_listener(File_Track_System *system, char *filename){
// different names that both refer to the same file. In this case we should // different names that both refer to the same file. In this case we should
// treat them as two separate files so that if one of the listeners is removed // treat them as two separate files so that if one of the listeners is removed
// the other on the same file keeps working. // the other on the same file keeps working.
// RELEASE LOCK if(tracking_system_has_space(tables, 1)){
size_t filename_len = strlen(filename) + 1;
if(vars->string_mem_end - vars->string_mem_begin >= filename_len){
// TODO(inso): which events do we want?
int wd = inotify_add_watch(vars->inotify, filename, IN_ALL_EVENTS);
if(wd != -1){
File_Index key = { wd, 1 };
File_Track_Entry *entry = tracking_system_lookup_entry(tables, key);
Linux_File_Track_Entry *linux_entry = (Linux_File_Track_Entry*) entry;
LINUX_FN_DEBUG("map %s to wd %d", filename, wd);
Assert(entry_is_available(entry));
linux_entry->hash = key;
linux_entry->filename = vars->string_mem_begin;
memcpy(vars->string_mem_begin, filename, filename_len);
vars->string_mem_begin += filename_len;
++tables->tracked_count;
} else {
result = FileTrack_FileSystemError;
}
} else {
result = FileTrack_OutOfListenerMemory;
}
} else {
result = FileTrack_OutOfTableMemory;
}
pthread_mutex_unlock(&vars->lock);
LINUX_FN_DEBUG("result: %d", result);
return(result); return(result);
} }
@ -111,16 +162,35 @@ FILE_TRACK_LINK File_Track_Result
remove_listener(File_Track_System *system, char *filename){ remove_listener(File_Track_System *system, char *filename){
File_Track_Result result = FileTrack_Good; File_Track_Result result = FileTrack_Good;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
File_Track_Tables *tables = to_tables(vars);
// ACQUIRE LOCK File_Track_Entry *entries = (File_Track_Entry*) to_ptr(tables, tables->file_table);
pthread_mutex_lock(&vars->lock);
// NOTE(allen): // NOTE(allen):
// Here do something to stop listening to changes to the file named filename. // Here do something to stop listening to changes to the file named filename.
// We assume this filename has been passed into add_listener already and that // We assume this filename has been passed into add_listener already and that
// if it has not it is a bug in the user's code not in this code. // if it has not it is a bug in the user's code not in this code.
// RELEASE LOCK for(uint32_t i = 0; i < tables->max; ++i){
Linux_File_Track_Entry *e = (Linux_File_Track_Entry*)(entries + i);
if(e->hash.id[1] != 1) continue;
if(strcmp(e->filename, filename) == 0){
LINUX_FN_DEBUG("%s found as wd %d", filename, e->hash.id[0]);
if(inotify_rm_watch(vars->inotify, e->hash.id[0]) == -1){
perror("inotify_rm_watch");
result = FileTrack_FileSystemError;
}
internal_free_slot(tables, (File_Track_Entry*)e);
// NOTE(inso): associated string memory in listeners would be freed here
break;
}
}
pthread_mutex_unlock(&vars->lock);
LINUX_FN_DEBUG("result: %d", result);
return(result); return(result);
} }
@ -128,9 +198,9 @@ FILE_TRACK_LINK File_Track_Result
move_track_system(File_Track_System *system, void *mem, int32_t size){ move_track_system(File_Track_System *system, void *mem, int32_t size){
File_Track_Result result = FileTrack_Good; File_Track_Result result = FileTrack_Good;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
// ACQUIRE LOCK pthread_mutex_lock(&vars->lock);
{ {
File_Track_Tables *original_tables = to_tables(vars); File_Track_Tables *original_tables = to_tables(vars);
result = move_table_memory(original_tables, mem, size); result = move_table_memory(original_tables, mem, size);
@ -138,9 +208,11 @@ move_track_system(File_Track_System *system, void *mem, int32_t size){
vars->tables = mem; vars->tables = mem;
} }
} }
// RELEASE LOCK pthread_mutex_unlock(&vars->lock);
LINUX_FN_DEBUG("size: %d, %d", size, result);
return(result); return(result);
} }
@ -148,16 +220,30 @@ FILE_TRACK_LINK File_Track_Result
expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size){ expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size){
File_Track_Result result = FileTrack_Good; File_Track_Result result = FileTrack_Good;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
// ACQUIRE LOCK pthread_mutex_lock(&vars->lock);
// NOTE(allen): If there is a data structure for the listeners // NOTE(allen): If there is a data structure for the listeners
// this call adds more memory to that system. If this system // this call adds more memory to that system. If this system
// wants to free old memory the API does not currently support // wants to free old memory the API does not currently support
// that but it can. // that but it can.
// RELEASE LOCK // NOTE(inso): pointer to old string mem is lost here.
// would need to keep it around if we want to free in the future
// NOTE(inso): assuming PATH_MAX is a reasonable lower bound of extra memory to get
if(size < PATH_MAX){
result = FileTrack_MemoryTooSmall;
} else {
vars->string_mem_begin = (char*) mem;
vars->string_mem_end = (char*) mem + size;;
}
pthread_mutex_unlock(&vars->lock);
LINUX_FN_DEBUG("size: %d, result: %d", size, result);
return(result); return(result);
} }
@ -165,9 +251,10 @@ FILE_TRACK_LINK File_Track_Result
get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t *size){ get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t *size){
File_Track_Result result = FileTrack_NoMoreEvents; File_Track_Result result = FileTrack_NoMoreEvents;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
File_Track_Tables *tables = to_tables(vars);
// ACQUIRE LOCK
pthread_mutex_lock(&vars->lock);
// NOTE(allen): If there are any new file changes report them // NOTE(allen): If there are any new file changes report them
// by copying the name of the file to the buffer and returning // by copying the name of the file to the buffer and returning
// FileTrack_Good. It is allowed for this system to report // FileTrack_Good. It is allowed for this system to report
@ -175,9 +262,36 @@ get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t *
// is up to the user to check the filename and see if it cares // is up to the user to check the filename and see if it cares
// about that file. If there are no new changes the return // about that file. If there are no new changes the return
// FileTrack_NoMoreEvents. // FileTrack_NoMoreEvents.
// RELEASE LOCK struct inotify_event ev;
ssize_t n = read(vars->inotify, &ev, sizeof(ev));
if(n == -1 && errno != EAGAIN){
perror("inotify read");
} else if(n > 0){
File_Index key = { ev.wd, 1 };
File_Track_Entry *entry = tracking_system_lookup_entry(tables, key);
Linux_File_Track_Entry *linux_entry = (Linux_File_Track_Entry*) entry;
if(!entry_is_available(entry)){
LINUX_FN_DEBUG("event from wd %d (%s)", ev.wd, linux_entry->filename);
size_t filename_size = strlen(linux_entry->filename);
if(max < filename_size){
result = FileTrack_MemoryTooSmall;
// NOTE(inso): this event will be dropped, needs to be stashed.
LINUX_FN_DEBUG("max too small, event dropped");
} else {
memcpy(buffer, linux_entry->filename, filename_size);
*size = filename_size;
result = FileTrack_Good;
}
} else {
LINUX_FN_DEBUG("dead event from wd %d", ev.wd);
}
}
pthread_mutex_unlock(&vars->lock);
return(result); return(result);
} }
@ -185,27 +299,16 @@ FILE_TRACK_LINK File_Track_Result
shut_down_track_system(File_Track_System *system){ shut_down_track_system(File_Track_System *system){
File_Track_Result result = FileTrack_Good; File_Track_Result result = FileTrack_Good;
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
// NOTE(allen): Close all the handles stored in the table.
{
File_Track_Tables *tables = to_tables(vars);
File_Track_Entry *entries = (File_Track_Entry*)to_ptr(tables, tables->file_table);
uint32_t max = tables->max;
for (uint32_t index = 0; index < max; ++index){
File_Track_Entry *entry = entries + index;
if (!entry_is_available(entry)){
// NOTE(allen): If table entries contain anything
// that need cleanup, that cleanup should happen here.
}
}
}
// NOTE(allen): Close all the global track system resources. // NOTE(allen): Close all the global track system resources.
if(close(vars->inotify) == -1){
result = FileTrack_FileSystemError;
}
pthread_mutex_destroy(&vars->lock);
LINUX_FN_DEBUG("result: %d", result);
return(result); return(result);
} }

View File

@ -69,8 +69,6 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/input.h> #include <linux/input.h>
#include "filetrack/4tech_file_track_linux.c"
#include "system_shared.h"
// //
// Linux macros // Linux macros
@ -105,6 +103,9 @@
#define InterlockedCompareExchange(dest, ex, comp) __sync_val_compare_and_swap((dest), (comp), (ex)) #define InterlockedCompareExchange(dest, ex, comp) __sync_val_compare_and_swap((dest), (comp), (ex))
#include "filetrack/4tech_file_track_linux.c"
#include "system_shared.h"
// //
// Linux structs / enums // Linux structs / enums
// //
@ -120,11 +121,10 @@ struct Sys_Bubble : public Bubble{
enum { enum {
LINUX_4ED_EVENT_X11 = (UINT64_C(1) << 32), LINUX_4ED_EVENT_X11 = (UINT64_C(1) << 32),
LINUX_4ED_EVENT_X11_INTERNAL = (UINT64_C(1) << 33), LINUX_4ED_EVENT_X11_INTERNAL = (UINT64_C(2) << 32),
LINUX_4ED_EVENT_FILE = (UINT64_C(1) << 34), LINUX_4ED_EVENT_STEP = (UINT64_C(3) << 32),
LINUX_4ED_EVENT_STEP = (UINT64_C(1) << 35), LINUX_4ED_EVENT_STEP_TIMER = (UINT64_C(4) << 32),
LINUX_4ED_EVENT_STEP_TIMER = (UINT64_C(1) << 36), LINUX_4ED_EVENT_CLI = (UINT64_C(5) << 32),
LINUX_4ED_EVENT_CLI = (UINT64_C(1) << 37),
}; };
struct Linux_Coroutine { struct Linux_Coroutine {
@ -268,11 +268,7 @@ static_assert(sizeof(Plat_Handle) >= sizeof(sem_t*), "Plat_Handle not big e
static_assert(sizeof(Plat_Handle) >= sizeof(int), "Plat_Handle not big enough"); static_assert(sizeof(Plat_Handle) >= sizeof(int), "Plat_Handle not big enough");
// //
// Linux shared/system functions // Shared system functions (system_shared.h)
//
//
// Memory
// //
internal void* internal void*
@ -341,9 +337,6 @@ Sys_Free_Memory_Sig(system_free_memory){
LinuxFreeMemory(block); LinuxFreeMemory(block);
} }
//
// File
//
internal internal
Sys_File_Can_Be_Made_Sig(system_file_can_be_made){ Sys_File_Can_Be_Made_Sig(system_file_can_be_made){
@ -368,49 +361,13 @@ Sys_Get_Binary_Path_Sig(system_get_binary_path){
} }
// //
// Linux application/system functions // System Functions (4ed_system.h)
// //
// //
// File // Files
// //
internal
Sys_File_Time_Stamp_Sig(system_file_time_stamp){
struct stat info = {};
u64 microsecond_timestamp = 0;
if(stat(filename, &info) == 0){
#if OLD_STAT_NANO_TIME
microsecond_timestamp =
(info.st_mtime * UINT64_C(1000000)) +
(info.st_mtimensec / UINT64_C(1000));
#else
microsecond_timestamp =
(info.st_mtim.tv_sec * UINT64_C(1000000)) +
(info.st_mtim.tv_nsec / UINT64_C(1000));
#endif
}
//LINUX_FN_DEBUG("%s = %" PRIu64, filename, microsecond_timestamp);
return(microsecond_timestamp);
}
internal
Sys_Now_Time_Stamp_Sig(system_now_time_stamp){
struct timespec spec;
u64 result;
clock_gettime(CLOCK_REALTIME, &spec);
result = (spec.tv_sec * UINT64_C(1000000)) + (spec.tv_nsec / UINT64_C(1000));
//LINUX_FN_DEBUG("ts: %" PRIu64, result);
return(result);
}
internal internal
Sys_Set_File_List_Sig(system_set_file_list){ Sys_Set_File_List_Sig(system_set_file_list){
DIR *d; DIR *d;
@ -503,105 +460,88 @@ Sys_Set_File_List_Sig(system_set_file_list){
} }
internal internal
Sys_File_Unique_Hash_Sig(system_file_unique_hash){ Sys_Get_Canonical_Sig(system_get_canonical){
Unique_Hash result = {}; int32_t result = 0;
struct stat st = {};
*success = 0;
if(stat(filename.str, &st) == 0){ char* path = realpath(strndupa(filename, len), NULL);
memcpy(&result, &st.st_dev, sizeof(st.st_dev)); if(!path){
memcpy((char*)&result + sizeof(st.st_dev), &st.st_ino, sizeof(st.st_ino)); perror("realpath");
*success = 1; } else {
size_t path_len = strlen(path);
if(max >= path_len){
memcpy(buffer, path, path_len);
result = path_len;
}
free(path);
} }
// LINUX_FN_DEBUG("%s = %ld:%ld", filename.str, (long)st.st_dev, (long)st.st_ino);
return result; return result;
} }
internal internal
Sys_File_Track_Sig(system_file_track){ Sys_Load_Handle_Sig(system_load_handle){
if(filename.size <= 0 || !filename.str) return; b32 result = 0;
char* fname = (char*) alloca(filename.size+1);
memcpy(fname, filename.str, filename.size);
fname[filename.size] = 0;
int wd = inotify_add_watch(linuxvars.inotify_fd, fname, IN_ALL_EVENTS);
if(wd == -1){
perror("inotify_add_watch");
} else {
printf("watch %s\n", fname);
// TODO: store the wd somewhere so Untrack can use it
}
}
internal
Sys_File_Untrack_Sig(system_file_untrack){
//TODO
// inotify_rm_watch(...)
}
internal
Sys_File_Load_Begin_Sig(system_file_load_begin){
File_Loading loading = {};
struct stat info = {};
LINUX_FN_DEBUG("%s", filename);
int fd = open(filename, O_RDONLY); int fd = open(filename, O_RDONLY);
if(fd < 0){ if(fd == -1){
fprintf(stderr, "sys_open_file: open '%s': %s\n", filename, strerror(errno)); perror("open");
goto out; } else {
} *(int*)handle_out = fd;
if(fstat(fd, &info) < 0){ result = 1;
fprintf(stderr, "sys_open_file: stat '%s': %s\n", filename, strerror(errno));
goto out;
}
if(info.st_size < 0){
fprintf(stderr, "sys_open_file: st_size < 0: %ld\n", info.st_size);
goto out;
} }
loading.handle = LinuxFDToHandle(fd); return result;
loading.size = info.st_size;
loading.exists = 1;
out:
if(fd != -1 && !loading.exists) close(fd);
return(loading);
} }
internal internal
Sys_File_Load_End_Sig(system_file_load_end){ Sys_Load_Size_Sig(system_load_size){
int fd = LinuxHandleToFD(loading.handle); u32 result = 0;
char* ptr = buffer;
size_t size = loading.size;
if(!loading.exists || fd == -1) return 0; int fd = *(int*)&handle;
struct stat st;
if(fstat(fd, &st) == -1){
perror("fstat");
} else {
result = st.st_size;
}
return result;
}
internal
Sys_Load_File_Sig(system_load_file){
int fd = *(int*)&handle;
do { do {
ssize_t read_result = read(fd, buffer, size); ssize_t n = read(fd, buffer, size);
if(read_result == -1){ if(n == -1 && errno != EAGAIN){
if(errno != EINTR){ perror("read");
perror("system_file_load_end"); break;
break;
}
} else { } else {
size -= read_result; size -= n;
ptr += read_result; buffer += n;
} }
} while(size); } while(size);
close(fd); return size == 0;
LINUX_FN_DEBUG("success == %d", (size == 0));
return (size == 0);
} }
internal internal
Sys_File_Save_Sig(system_file_save){ Sys_Load_Close_Sig(system_load_close){
b32 result = 1;
int fd = *(int*)&handle;
if(close(fd) == -1){
perror("close");
result = 0;
}
return result;
}
internal
Sys_Save_File_Sig(system_save_file){
b32 result = 0; b32 result = 0;
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 00640); int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 00640);
@ -630,7 +570,24 @@ Sys_File_Save_Sig(system_file_save){
} }
// //
// Custom access to OS pages // Time
//
internal
Sys_Now_Time_Sig(system_now_time){
struct timespec spec;
u64 result;
clock_gettime(CLOCK_REALTIME, &spec);
result = (spec.tv_sec * UINT64_C(1000000)) + (spec.tv_nsec / UINT64_C(1000));
//LINUX_FN_DEBUG("ts: %" PRIu64, result);
return(result);
}
//
// 4coder_custom.h
// //
internal internal
@ -692,10 +649,6 @@ MEMORY_FREE_SIG(system_memory_free){
munmap(mem, size); munmap(mem, size);
} }
//
// Filesystem navigation
//
internal internal
FILE_EXISTS_SIG(system_file_exists){ FILE_EXISTS_SIG(system_file_exists){
int result = 0; int result = 0;
@ -983,175 +936,26 @@ Sys_CLI_End_Update_Sig(system_cli_end_update){
// Threads // Threads
// //
//#define OLD_JOB_QUEUE internal
Sys_Acquire_Lock_Sig(system_acquire_lock){
#ifdef OLD_JOB_QUEUE pthread_mutex_lock(linuxvars.locks + id);
internal void*
JobThreadProc(void* arg){
Thread_Context *thread = (Thread_Context*)arg;
Work_Queue *queue = linuxvars.queues + thread->group_id;
Thread_Group *group = linuxvars.groups + thread->group_id;
i32 thread_index = thread->id - 1;
i32 cancel_lock = group->cancel_lock0 + thread_index;
i32 cancel_cv = group->cancel_cv0 + thread_index;
Thread_Memory *thread_memory = linuxvars.thread_memory + thread_index;
if (thread_memory->size == 0){
i32 new_size = Kbytes(64);
thread_memory->data = LinuxGetMemory(new_size);
thread_memory->size = new_size;
}
for (;;){
u32 read_index = queue->read_position;
u32 write_index = queue->write_position;
if (read_index != write_index){
u32 next_read_index = (read_index + 1) % QUEUE_WRAP;
u32 safe_read_index =
__sync_val_compare_and_swap(&queue->read_position,
read_index, next_read_index);
if (safe_read_index == read_index){
Full_Job_Data *full_job = queue->jobs + safe_read_index;
// NOTE(allen): This is interlocked so that it plays nice
// with the cancel job routine, which may try to cancel this job
// at the same time that we try to run it
i32 safe_running_thread =
__sync_val_compare_and_swap(&full_job->running_thread,
THREAD_NOT_ASSIGNED, thread->id);
if (safe_running_thread == THREAD_NOT_ASSIGNED){
thread->job_id = full_job->id;
thread->running = 1;
full_job->job.callback(&linuxvars.system, thread, thread_memory, full_job->job.data);
full_job->running_thread = 0;
thread->running = 0;
system_acquire_lock(cancel_lock);
if(thread->cancel){
thread->cancel = 0;
pthread_cond_signal(linuxvars.conds + cancel_cv);
}
system_release_lock(cancel_lock);
LinuxScheduleStep();
}
}
}
else{
sem_wait(LinuxHandleToSem(queue->semaphore));
}
}
} }
internal internal
Sys_Post_Job_Sig(system_post_job){ Sys_Release_Lock_Sig(system_release_lock){
Work_Queue *queue = linuxvars.queues + group_id; pthread_mutex_unlock(linuxvars.locks + id);
Assert((queue->write_position + 1) % QUEUE_WRAP != queue->read_position % QUEUE_WRAP);
b32 success = 0;
u32 result = 0;
while (!success){
u32 write_index = queue->write_position;
u32 next_write_index = (write_index + 1) % QUEUE_WRAP;
u32 safe_write_index =
__sync_val_compare_and_swap(&queue->write_position,
write_index, next_write_index);
if (safe_write_index == write_index){
result = write_index;
write_index = write_index % QUEUE_WRAP;
queue->jobs[write_index].job = job;
queue->jobs[write_index].running_thread = THREAD_NOT_ASSIGNED;
queue->jobs[write_index].id = result;
success = 1;
}
}
sem_post(LinuxHandleToSem(queue->semaphore));
return result;
} }
internal internal void
Sys_Cancel_Job_Sig(system_cancel_job){ system_wait_cv(i32 lock_id, i32 cv_id){
Work_Queue *queue = linuxvars.queues + group_id; pthread_cond_wait(linuxvars.conds + cv_id, linuxvars.locks + lock_id);
Thread_Group *group = linuxvars.groups + group_id;
u32 job_index = job_id % QUEUE_WRAP;
Full_Job_Data *full_job = queue->jobs + job_index;
Assert(full_job->id == job_id);
u32 thread_id =
__sync_val_compare_and_swap(&full_job->running_thread,
THREAD_NOT_ASSIGNED, 0);
if (thread_id != THREAD_NOT_ASSIGNED && thread_id != 0){
i32 thread_index = thread_id - 1;
i32 cancel_lock = group->cancel_lock0 + thread_index;
i32 cancel_cv = group->cancel_cv0 + thread_index;
Thread_Context *thread = group->threads + thread_index;
system_acquire_lock(cancel_lock);
thread->cancel = 1;
system_release_lock(FRAME_LOCK);
do {
pthread_cond_wait(linuxvars.conds + cancel_cv, linuxvars.locks + cancel_lock);
} while(thread->cancel == 1);
system_acquire_lock(FRAME_LOCK);
system_release_lock(cancel_lock);
LinuxScheduleStep();
}
} }
internal internal void
Sys_Check_Cancel_Sig(system_check_cancel){ system_signal_cv(i32 lock_id, i32 cv_id){
b32 result = 0; pthread_cond_signal(linuxvars.conds + cv_id);
Thread_Group* group = linuxvars.groups + thread->group_id;
i32 thread_index = thread->id - 1;
i32 cancel_lock = group->cancel_lock0 + thread_index;
system_acquire_lock(cancel_lock);
if (thread->cancel){
result = 1;
}
system_release_lock(cancel_lock);
return (result);
} }
internal
Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
void *old_data;
i32 old_size, new_size;
system_acquire_lock(CANCEL_LOCK0 + memory->id - 1);
old_data = memory->data;
old_size = memory->size;
new_size = LargeRoundUp(memory->size*2, Kbytes(4));
memory->data = system_get_memory(new_size);
memory->size = new_size;
if (old_data){
memcpy(memory->data, old_data, old_size);
system_free_memory(old_data);
}
system_release_lock(CANCEL_LOCK0 + memory->id - 1);
}
#else // new job queue
internal void* internal void*
JobThreadProc(void* lpParameter){ JobThreadProc(void* lpParameter){
Thread_Context *thread = (Thread_Context*)lpParameter; Thread_Context *thread = (Thread_Context*)lpParameter;
@ -1441,28 +1245,6 @@ Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
system_release_lock(CANCEL_LOCK0 + memory->id - 1); system_release_lock(CANCEL_LOCK0 + memory->id - 1);
} }
#endif // OLD_JOB_QUEUE
internal
Sys_Acquire_Lock_Sig(system_acquire_lock){
pthread_mutex_lock(linuxvars.locks + id);
}
internal
Sys_Release_Lock_Sig(system_release_lock){
pthread_mutex_unlock(linuxvars.locks + id);
}
internal void
system_wait_cv(i32 lock_id, i32 cv_id){
pthread_cond_wait(linuxvars.conds + cv_id, linuxvars.locks + lock_id);
}
internal void
system_signal_cv(i32 lock_id, i32 cv_id){
pthread_cond_signal(linuxvars.conds + cv_id);
}
// //
// Debug // Debug
// //
@ -1607,17 +1389,23 @@ LinuxLoadAppCode(String* base_dir){
internal void internal void
LinuxLoadSystemCode(){ LinuxLoadSystemCode(){
linuxvars.system.file_time_stamp = system_file_time_stamp; // files
linuxvars.system.now_time_stamp = system_now_time_stamp;
linuxvars.system.set_file_list = system_set_file_list; linuxvars.system.set_file_list = system_set_file_list;
linuxvars.system.file_unique_hash = system_file_unique_hash; linuxvars.system.get_canonical = system_get_canonical;
linuxvars.system.file_track = system_file_track; linuxvars.system.add_listener = system_add_listener;
linuxvars.system.file_untrack = system_file_untrack; linuxvars.system.remove_listener = system_remove_listener;
linuxvars.system.file_load_begin = system_file_load_begin; linuxvars.system.get_file_change = system_get_file_change;
linuxvars.system.file_load_end = system_file_load_end; linuxvars.system.load_handle = system_load_handle;
linuxvars.system.file_save = system_file_save; linuxvars.system.load_size = system_load_size;
linuxvars.system.load_file = system_load_file;
linuxvars.system.load_close = system_load_close;
linuxvars.system.save_file = system_save_file;
// time
linuxvars.system.now_time = system_now_time;
// 4coder_custom.h
linuxvars.system.memory_allocate = system_memory_allocate; linuxvars.system.memory_allocate = system_memory_allocate;
linuxvars.system.memory_set_protection = system_memory_set_protection; linuxvars.system.memory_set_protection = system_memory_set_protection;
linuxvars.system.memory_free = system_memory_free; linuxvars.system.memory_free = system_memory_free;
@ -1626,18 +1414,22 @@ LinuxLoadSystemCode(){
linuxvars.system.get_4ed_path = system_get_4ed_path; linuxvars.system.get_4ed_path = system_get_4ed_path;
linuxvars.system.show_mouse_cursor = system_show_mouse_cursor; linuxvars.system.show_mouse_cursor = system_show_mouse_cursor;
// clipboard
linuxvars.system.post_clipboard = system_post_clipboard; linuxvars.system.post_clipboard = system_post_clipboard;
// coroutine
linuxvars.system.create_coroutine = system_create_coroutine; linuxvars.system.create_coroutine = system_create_coroutine;
linuxvars.system.launch_coroutine = system_launch_coroutine; linuxvars.system.launch_coroutine = system_launch_coroutine;
linuxvars.system.resume_coroutine = system_resume_coroutine; linuxvars.system.resume_coroutine = system_resume_coroutine;
linuxvars.system.yield_coroutine = system_yield_coroutine; linuxvars.system.yield_coroutine = system_yield_coroutine;
// cli
linuxvars.system.cli_call = system_cli_call; linuxvars.system.cli_call = system_cli_call;
linuxvars.system.cli_begin_update = system_cli_begin_update; linuxvars.system.cli_begin_update = system_cli_begin_update;
linuxvars.system.cli_update_step = system_cli_update_step; linuxvars.system.cli_update_step = system_cli_update_step;
linuxvars.system.cli_end_update = system_cli_end_update; linuxvars.system.cli_end_update = system_cli_end_update;
// threads
linuxvars.system.post_job = system_post_job; linuxvars.system.post_job = system_post_job;
linuxvars.system.cancel_job = system_cancel_job; linuxvars.system.cancel_job = system_cancel_job;
linuxvars.system.check_cancel = system_check_cancel; linuxvars.system.check_cancel = system_check_cancel;
@ -1645,12 +1437,14 @@ LinuxLoadSystemCode(){
linuxvars.system.acquire_lock = system_acquire_lock; linuxvars.system.acquire_lock = system_acquire_lock;
linuxvars.system.release_lock = system_release_lock; linuxvars.system.release_lock = system_release_lock;
// debug
#if FRED_INTERNAL #if FRED_INTERNAL
linuxvars.system.internal_sentinel = internal_sentinel; linuxvars.system.internal_sentinel = internal_sentinel;
linuxvars.system.internal_get_thread_states = internal_get_thread_states; linuxvars.system.internal_get_thread_states = internal_get_thread_states;
linuxvars.system.internal_debug_message = internal_debug_message; linuxvars.system.internal_debug_message = internal_debug_message;
#endif #endif
// non-function details
linuxvars.system.slash = '/'; linuxvars.system.slash = '/';
} }
@ -2226,7 +2020,7 @@ LinuxStringDup(String* str, void* data, size_t size){
internal void internal void
LinuxScheduleStep(void) LinuxScheduleStep(void)
{ {
u64 now = system_now_time_stamp(); u64 now = system_now_time();
u64 diff = (now - linuxvars.last_step); u64 diff = (now - linuxvars.last_step);
if(diff > (u64)frame_useconds){ if(diff > (u64)frame_useconds){
@ -2924,23 +2718,6 @@ LinuxHandleX11Events(void)
} }
} }
//
// inotify utility func
//
internal void
LinuxHandleFileEvents()
{
struct inotify_event* e;
char buff[sizeof(*e) + NAME_MAX + 1];
ssize_t num_bytes = read(linuxvars.inotify_fd, buff, sizeof(buff));
for(char* p = buff; (p - buff) < num_bytes; p += (sizeof(*e) + e->len)){
e = (struct inotify_event*)p;
// TODO: do something with the e->wd / e->name & report that in app.step?
}
}
// //
// Entry point // Entry point
// //
@ -2979,7 +2756,9 @@ main(int argc, char **argv)
memory_vars.user_memory = system_get_memory(memory_vars.user_memory_size); memory_vars.user_memory = system_get_memory(memory_vars.user_memory_size);
linuxvars.target.max = Mbytes(1); linuxvars.target.max = Mbytes(1);
linuxvars.target.push_buffer = (byte*)system_get_memory(linuxvars.target.max); linuxvars.target.push_buffer = (char*)system_get_memory(linuxvars.target.max);
init_shared_vars();
// //
// Read command line // Read command line
@ -3248,9 +3027,6 @@ main(int argc, char **argv)
e.data.u64 = LINUX_4ED_EVENT_X11; e.data.u64 = LINUX_4ED_EVENT_X11;
epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.x11_fd, &e); epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.x11_fd, &e);
e.data.u64 = LINUX_4ED_EVENT_FILE;
epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.inotify_fd, &e);
e.data.u64 = LINUX_4ED_EVENT_STEP; e.data.u64 = LINUX_4ED_EVENT_STEP;
epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_event_fd, &e); epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_event_fd, &e);
@ -3321,10 +3097,6 @@ main(int argc, char **argv)
XProcessInternalConnection(linuxvars.XDisplay, fd); XProcessInternalConnection(linuxvars.XDisplay, fd);
} break; } break;
case LINUX_4ED_EVENT_FILE: {
LinuxHandleFileEvents();
} break;
case LINUX_4ED_EVENT_STEP: { case LINUX_4ED_EVENT_STEP: {
u64 ev; u64 ev;
int ret; int ret;
@ -3350,7 +3122,7 @@ main(int argc, char **argv)
} }
if(do_step){ if(do_step){
linuxvars.last_step = system_now_time_stamp(); linuxvars.last_step = system_now_time();
if(linuxvars.input.first_step || !linuxvars.has_xfixes){ if(linuxvars.input.first_step || !linuxvars.has_xfixes){
XConvertSelection( XConvertSelection(