diff --git a/4coder_default_bindings.cpp b/4coder_default_bindings.cpp index f7c2443d..bd96e05e 100644 --- a/4coder_default_bindings.cpp +++ b/4coder_default_bindings.cpp @@ -261,8 +261,8 @@ default_keys(Bind_Helper *context){ bind(context, '.', MDFR_ALT, change_to_build_panel); bind(context, ',', MDFR_ALT, close_build_panel); - bind(context, 'n', MDFR_ALT, goto_next_error_no_skips); - bind(context, 'N', MDFR_ALT, goto_prev_error_no_skips); + bind(context, 'n', MDFR_ALT, goto_next_error); + bind(context, 'N', MDFR_ALT, goto_prev_error); bind(context, 'M', MDFR_ALT, goto_first_error); bind(context, 'm', MDFR_ALT, build_search); @@ -397,10 +397,10 @@ default_keys(Bind_Helper *context){ end_map(context); } + + #ifndef NO_BINDING - - extern "C" int get_bindings(void *data, int size){ Bind_Helper context_ = begin_bind_helper(data, size); diff --git a/4coder_default_include.cpp b/4coder_default_include.cpp index 3edf8d21..a6728866 100644 --- a/4coder_default_include.cpp +++ b/4coder_default_include.cpp @@ -2516,7 +2516,7 @@ CUSTOM_COMMAND_SIG(word_complete){ &complete_state.iter); if (match.found_match){ - int match_size = match.end - match.start; + match_size = match.end - match.start; Temp_Memory temp = begin_temp_memory(&global_part); char *spare = push_array(&global_part, char, match_size); @@ -2698,8 +2698,8 @@ SCROLL_RULE_SIG(smooth_scroll_rule){ // If this hook is not implemented a default behavior of calling the // command is used. It is important to note that paste_next does not // work without this hook. -// NOTE(allen|a4.0.10): As of this version the word_complete command also -// relies on this particular command caller hook. +// NOTE(allen|a4.0.10): As of this version the word_complete command +// also relies on this particular command caller hook. COMMAND_CALLER_HOOK(default_command_caller){ View_Summary view = app->get_active_view(app, AccessAll); @@ -2714,3 +2714,4 @@ COMMAND_CALLER_HOOK(default_command_caller){ } #endif + diff --git a/4coder_helper.h b/4coder_helper.h index 51e97164..bfe2aebb 100644 --- a/4coder_helper.h +++ b/4coder_helper.h @@ -374,7 +374,7 @@ query_user_general(Application_Links *app, Query_Bar *bar, int force_number){ } } } - + // NOTE(allen|a3.4.4): All we have to do to update the query bar is edit our // local Query_Bar struct! This is handy because it means our Query_Bar // is always correct for typical use without extra work updating the bar. @@ -393,6 +393,8 @@ query_user_general(Application_Links *app, Query_Bar *bar, int force_number){ } } + terminate_with_null(&bar->string); + return(success); } diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index cfd5756f..ca2bd632 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -3295,7 +3295,7 @@ view_new_file(System_Functions *system, Models *models, file->settings.is_initialized = 1; #if BUFFER_EXPERIMENT_SCALPEL <= 0 - if (file->settings.tokens_exist){ + if (file->settings.tokens_exist && file->state.token_stack.tokens == 0){ file_first_lex_parallel(system, general, file); } #endif @@ -3310,7 +3310,7 @@ init_normal_file(System_Functions *system, Models *models, Editing_File *file, String val = make_string(buffer, size); file_create_from_string(system, models, file, file->name.source_path.str, val); - if (file->settings.tokens_exist){ + if (file->settings.tokens_exist && file->state.token_stack.tokens == 0){ file_first_lex_parallel(system, general, file); } diff --git a/filetrack/4tech_file_track.h b/filetrack/4tech_file_track.h new file mode 100644 index 00000000..57a9ddda --- /dev/null +++ b/filetrack/4tech_file_track.h @@ -0,0 +1,102 @@ +/* + +Copy Right FourTech LLC, 2016 +All Rights Are Reserved + +The OS agnostic file tracking API for applications +that want to interact with potentially many files on +the disk that could be changed by other applications. + +Created on: 20.07.2016 + +*/ + +// TOP + +#ifndef FILE_TRACK_4TECH_H +#define FILE_TRACK_4TECH_H + +#include + +enum{ + FileTrack_Good, + FileTrack_MemoryTooSmall, + FileTrack_OutOfTableMemory, + FileTrack_OutOfListenerMemory, + FileTrack_FileNotFound, + FileTrack_FileAlreadyTracked, + FileTrack_FileNotTracked, + FileTrack_NoMoreEvents, + FileTrack_FileSystemError +}; + +typedef struct{ + uint8_t opaque[128]; +} File_Track_System; + +typedef struct{ + uint32_t id[4]; +} File_Index; + +static File_Index +zero_file_index(){ + File_Index a = {0}; + return(a); +} + +static int32_t +file_index_eq(File_Index a, File_Index b){ + return ((a.id[0] == b.id[0]) && + (a.id[1] == b.id[1]) && + (a.id[2] == b.id[2]) && + (a.id[3] == b.id[3])); +} + +typedef int32_t File_Track_Result; +typedef uint64_t File_Time; + +File_Track_Result +init_track_system(File_Track_System *system, + void *table_memory, int32_t table_memory_size, + void *listener_memory, int32_t listener_memory_size); + +File_Track_Result +begin_tracking_file(File_Track_System *system, char *name, File_Index *index, File_Time *time); + +File_Track_Result +get_tracked_file_index(File_Track_System *system, char *name, File_Index *index); + +File_Track_Result +stop_tracking_file(File_Track_System *system, File_Index index); + +File_Track_Result +count_tracked_files(File_Track_System *system, int32_t *count); + +File_Track_Result +get_tracked_file_time(File_Track_System *system, File_Index index, File_Time *time); + +File_Track_Result +move_track_system(File_Track_System *system, void *mem, int32_t size); + +File_Track_Result +expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size); + +File_Track_Result +get_change_event(File_Track_System *system, File_Index *index); + +File_Track_Result +get_tracked_file_size(File_Track_System *system, File_Index index, uint32_t *size); + +File_Track_Result +get_tracked_file_data(File_Track_System *system, File_Index index, void *mem, uint32_t size); + +File_Track_Result +rewrite_tracked_file(File_Track_System *system, File_Index index, + void *data, int32_t size, File_Time *time); + +File_Track_Result +shut_down_track_system(File_Track_System *system); + +#endif + +// BOTTOM diff --git a/filetrack/4tech_file_track_win32.c b/filetrack/4tech_file_track_win32.c new file mode 100644 index 00000000..11f27656 --- /dev/null +++ b/filetrack/4tech_file_track_win32.c @@ -0,0 +1,1071 @@ +/* + +Copy Right FourTech LLC, 2016 +All Rights Are Reserved + +The OS agnostic file tracking API for applications +that want to interact with potentially many files on +the disk that could be changed by other applications. + +Created on: 20.07.2016 + +*/ + +// TOP + +#include "4tech_file_track.h" + +#include + +#define Assert(c) do { if (!(c)) { *((int*)0) = 0xA11E; } } while (0) +#define ZeroStruct(s) for (int32_t i = 0; i < sizeof(s); ++i) { ((char*)(&(s)))[i] = 0; } + +#define NotImplemented Assert(!"not implemented") + +typedef uint32_t rptr32; + +typedef struct DLL_Node { + struct DLL_Node *next; + struct DLL_Node *prev; +} DLL_Node; + +typedef struct { + OVERLAPPED overlapped; + HANDLE dir; + int32_t user_count; + + char dir_name[512]; + + // TODO(allen): I am only ever using one thread + // for reading results. So is it possible to + // have them all go through the same location + // instead of having a different 2K block + // for each directory node? + char result[2048]; +} Directory_Listener; + +typedef struct { + DLL_Node node; + Directory_Listener listener; +} Directory_Listener_Node; + +typedef struct { + HANDLE iocp; + HANDLE thread; + CRITICAL_SECTION table_lock; + + void *tables; + DLL_Node free_sentinel; + +} File_Track_Vars; + +typedef struct { + int32_t size; + uint32_t tracked_count; + uint32_t tracked_dir_count; + uint32_t max; + uint32_t change_write_pos; + uint32_t change_read_pos; + rptr32 change_queue; + rptr32 file_table; +} File_Track_Tables; + +typedef struct { + HANDLE file, dir; + File_Index hash; + union { + Directory_Listener_Node *listener_node; + struct { + int32_t change_pos; + int32_t skip_change; + }; + }; +} File_Track_Entry; + +typedef struct { + File_Index index; + int32_t still_active; +} File_Change_Record; + +#define FILE_ENTRY_COST (sizeof(File_Change_Record) + sizeof(File_Track_Entry)) + +#define to_vars_(s) ((File_Track_Vars*)(s)) +#define to_tables(v) ((File_Track_Tables*)(v->tables)) +#define to_ptr(b,p) ((void*)((char*)b + p)) +#define to_rptr32(b,p) ((rptr32)((char*)(p) - (char*)(b))) + +static void +insert_node(DLL_Node *pos, DLL_Node *node){ + node->prev = pos; + node->next = pos->next; + pos->next = node; + node->next->prev = node; +} + +static void +remove_node(DLL_Node *node){ + node->next->prev = node->prev; + node->prev->next = node->next; +} + +static void +init_sentinel_node(DLL_Node *node){ + node->next = node; + node->prev = node; +} + +static DLL_Node* +allocate_node(DLL_Node *sentinel){ + DLL_Node *result = 0; + if (sentinel->next != sentinel){ + result = sentinel->next; + remove_node(result); + } + return(result); +} + +static int32_t +file_hash_is_zero(File_Index a){ + return ((a.id[0] == 0) && + (a.id[1] == 0) && + (a.id[2] == 0) && + (a.id[3] == 0)); +} + +static int32_t +file_hash_is_deleted(File_Index a){ + return ((a.id[0] == 0xFFFFFFFF) && + (a.id[1] == 0xFFFFFFFF) && + (a.id[2] == 0xFFFFFFFF) && + (a.id[3] == 0xFFFFFFFF)); +} + +static int32_t +tracking_system_has_space(File_Track_Tables *tables, int32_t new_count){ + uint32_t count = tables->tracked_count; + uint32_t max = tables->max; + int32_t result = ((count + new_count)*8 < max*7); + return(result); +} + +static int32_t +entry_is_available(File_Track_Entry *entry){ + int32_t result = 0; + if (entry){ + result = + file_hash_is_zero(entry->hash) || + file_hash_is_deleted(entry->hash); + } + return (result); +} + +typedef struct{ + File_Track_Entry *entry; +} File_Lookup_Result; + +static File_Lookup_Result +tracking_system_lookup_entry(File_Track_Tables *tables, File_Index key){ + uint32_t hash = key.id[0]; + uint32_t max = tables->max; + uint32_t index = (hash) % max; + uint32_t start = index; + + File_Track_Entry *entries = (File_Track_Entry*)to_ptr(tables, tables->file_table); + + File_Lookup_Result result = {0}; + + for (;;){ + File_Track_Entry *entry = entries + index; + + if (file_index_eq(entry->hash, key)){ + result.entry = entry; + break; + } + else if (file_hash_is_zero(entry->hash)){ + if (result.entry == 0){ + result.entry = entry; + } + break; + } + else if (file_hash_is_deleted(entry->hash)){ + if (result.entry == 0){ + result.entry = entry; + } + } + + ++index; + if (index == max) index = 0; + if (index == start) break; + } + + return(result); +} + +static File_Track_Entry* +get_file_entry(File_Track_Tables *tables, File_Index index){ + File_Track_Entry *entry = 0; + + File_Lookup_Result result = tracking_system_lookup_entry(tables, index); + if (result.entry && file_index_eq(index, result.entry->hash)){ + entry = result.entry; + } + + return(entry); +} + +File_Track_Result +internal_get_tracked_file_index(File_Track_Tables *tables, + char *name, + File_Index *index, + File_Track_Entry **entry); + +static DWORD +directory_watching(LPVOID ptr){ + File_Track_Vars *vars = to_vars_(ptr); + OVERLAPPED *overlapped = 0; + DWORD length = 0; + ULONG_PTR key = 0; + + for (;;){ + GetQueuedCompletionStatus( + vars->iocp, + &length, + &key, + &overlapped, + INFINITE + ); + + Directory_Listener *listener_ptr = (Directory_Listener*)overlapped; + Directory_Listener listener = *listener_ptr; + + ZeroStruct(listener_ptr->overlapped); + ReadDirectoryChangesW(listener_ptr->dir, + listener_ptr->result, + sizeof(listener_ptr->result), + 0, + FILE_NOTIFY_CHANGE_LAST_WRITE, + 0, + &listener_ptr->overlapped, + 0); + + { + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + File_Change_Record *records = to_ptr(tables, tables->change_queue); + + char *buffer = listener.result; + DWORD offset = 0; + FILE_NOTIFY_INFORMATION *info = 0; + + for (;;){ + info = (FILE_NOTIFY_INFORMATION*)(buffer + offset); + + // TODO(allen): make this real + int32_t success = 0; + char filename[512]; + int32_t len = info->FileNameLength / 2; + int32_t pos = 0; + + char *src = listener.dir_name; + for (int32_t i = 0; src[i]; ++i, ++pos){ + filename[pos] = src[i]; + } + + if (len + pos + 1 < sizeof(filename)){ + filename[pos++] = '/'; + + for (int32_t i = 0; i < len; ++i, ++pos){ + filename[pos] = (char)info->FileName[i]; + } + filename[pos] = 0; + + success = 1; + } + + if (success){ + File_Index change_index = zero_file_index(); + File_Track_Entry *entry = 0; + File_Track_Result result = + internal_get_tracked_file_index(tables, filename, &change_index, &entry); + + if (result == FileTrack_Good){ + BY_HANDLE_FILE_INFORMATION info = {0}; + + if (GetFileInformationByHandle(entry->file, &info)){ + if (entry->skip_change){ + entry->skip_change = 0; + } + else{ + File_Change_Record *record = 0; + + if (entry->change_pos == -1){ + int32_t write_pos = tables->change_write_pos; + if (tables->change_write_pos + 1 == tables->change_read_pos){ + break; + } + + tables->change_write_pos += 1; + entry->change_pos = write_pos; + + record = records + write_pos; + } + else{ + record = records + entry->change_pos; + } + + record->index = entry->hash; + record->still_active = 1; + } + } + } + } + + if (info->NextEntryOffset != 0){ + offset += info->NextEntryOffset; + } + else{ + break; + } + } + + LeaveCriticalSection(&vars->table_lock); + } + } +} + +File_Track_Result +init_track_system(File_Track_System *system, + void *table_memory, int32_t table_memory_size, + void *listener_memory, int32_t listener_memory_size){ + File_Track_Result result = FileTrack_MemoryTooSmall; + File_Track_Vars *vars = to_vars_(system); + + if (sizeof(File_Track_Tables) + FILE_ENTRY_COST*8 <= table_memory_size && + sizeof(Directory_Listener_Node) <= listener_memory_size){ + vars->tables = table_memory; + + File_Track_Tables *tables = to_tables(vars); + + // NOTE(allen): Initialize main data tables + { + tables->size = table_memory_size; + tables->tracked_count = 0; + tables->tracked_dir_count = 0; + + int32_t likely_entry_size = FILE_ENTRY_COST; + int32_t max_number_of_entries = (table_memory_size - sizeof(*tables)) / likely_entry_size; + + tables->change_queue = sizeof(*tables); + tables->file_table = tables->change_queue + + sizeof(File_Change_Record)*max_number_of_entries; + tables->max = max_number_of_entries; + } + + // NOTE(allen): Initialize nodes of directory watching + { + init_sentinel_node(&vars->free_sentinel); + + Directory_Listener_Node *listener = (Directory_Listener_Node*)listener_memory; + int32_t count = listener_memory_size / sizeof(Directory_Listener_Node); + for (int32_t i = 0; i < count; ++i, ++listener){ + insert_node(&vars->free_sentinel, &listener->node); + } + } + + // NOTE(allen): Launch the file watching thread + { + InitializeCriticalSection(&vars->table_lock); + + vars->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1); + + vars->thread = CreateThread(0, 0, + directory_watching, + system, + 0, 0); + } + + result = FileTrack_Good; + } + + return(result); +} + +static int32_t +internal_get_parent_name(char *out, int32_t max, char *name){ + int32_t len, slash_i; + + char *ptr = name; + for (; *ptr != 0; ++ptr); + len = (int32_t)(ptr - name); + + // TODO(allen): make this system real + Assert(len < max); + + for (slash_i = len-1; + slash_i > 0 && name[slash_i] != '\\' && name[slash_i] != '/'; + --slash_i); + + for (int32_t i = 0; i < slash_i; ++i){ + out[i] = name[i]; + } + out[slash_i] = 0; + + return(slash_i); +} + +static File_Index +internal_get_file_index(BY_HANDLE_FILE_INFORMATION info){ + File_Index hash; + hash.id[0] = info.nFileIndexLow; + hash.id[1] = info.nFileIndexHigh; + hash.id[2] = info.dwVolumeSerialNumber; + hash.id[3] = 0; + return(hash); +} + +static void +internal_set_time(File_Time *time, BY_HANDLE_FILE_INFORMATION info){ + *time = (((uint64_t)info.ftLastWriteTime.dwHighDateTime << 32) | + (info.ftLastWriteTime.dwLowDateTime)); +} + +static void +internal_free_slot(File_Track_Tables *tables, File_Track_Entry *entry){ + Assert(!entry_is_available(entry)); + + ZeroStruct(*entry); + entry->hash.id[0] = 0xFFFFFFFF; + entry->hash.id[1] = 0xFFFFFFFF; + entry->hash.id[2] = 0xFFFFFFFF; + entry->hash.id[3] = 0xFFFFFFFF; + + --tables->tracked_count; +} + +File_Track_Result +begin_tracking_file(File_Track_System *system, + char *name, + File_Index *index, + File_Time *time){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + // TODO(allen): Try multiple ways to open the + // file and get as many privledges as possible. + // Expand the API to be capable of reporting + // what privledges are available on a file. + HANDLE file = CreateFile( + name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); + + if (file != INVALID_HANDLE_VALUE){ + char dir_name[1024]; + int32_t dir_name_len = + internal_get_parent_name(dir_name, sizeof(dir_name), name); + + HANDLE dir = CreateFile( + dir_name, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, + 0); + + if (dir != INVALID_HANDLE_VALUE){ + BY_HANDLE_FILE_INFORMATION info = {0}; + BY_HANDLE_FILE_INFORMATION dir_info = {0}; + + if (GetFileInformationByHandle(dir, &dir_info)){ + File_Index dir_hash = internal_get_file_index(dir_info); + + File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash); + + if (GetFileInformationByHandle(file, &info)){ + File_Index hash = internal_get_file_index(info); + + if (entry_is_available(dir_lookup.entry)){ + if (tracking_system_has_space(tables, 2)){ + Directory_Listener_Node *node = (Directory_Listener_Node*) + allocate_node(&vars->free_sentinel); + if (node){ + if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){ + ZeroStruct(node->listener.overlapped); + if (ReadDirectoryChangesW(dir, + node->listener.result, + sizeof(node->listener.result), + 0, + FILE_NOTIFY_CHANGE_LAST_WRITE, + 0, + &node->listener.overlapped, + 0)){ + node->listener.dir = dir; + node->listener.user_count = 1; + + // TODO(allen): make this real! + Assert(dir_name_len < sizeof(node->listener.dir_name)); + for (int32_t i = 0; i < dir_name_len; ++i){ + node->listener.dir_name[i] = dir_name[i]; + } + node->listener.dir_name[dir_name_len] = 0; + + dir_lookup.entry->hash = dir_hash; + dir_lookup.entry->file = dir; + dir_lookup.entry->dir = dir; + dir_lookup.entry->listener_node = node; + ++tables->tracked_count; + ++tables->tracked_dir_count; + + File_Lookup_Result lookup = + tracking_system_lookup_entry(tables, hash); + Assert(entry_is_available(lookup.entry)); + + lookup.entry->hash = hash; + lookup.entry->file = file; + lookup.entry->dir = node->listener.dir; + lookup.entry->change_pos = -1; + ++tables->tracked_count; + + *index = hash; + internal_set_time(time, info); + } + else{ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileSystemError; + } + + if (result != FileTrack_Good){ + insert_node(&vars->free_sentinel, &node->node); + } + } + else{ + result = FileTrack_OutOfListenerMemory; + } + } + else{ + result = FileTrack_OutOfTableMemory; + } + } + else{ + if (tracking_system_has_space(tables, 1)){ + File_Lookup_Result lookup = tracking_system_lookup_entry(tables, hash); + if (entry_is_available(lookup.entry)){ + Directory_Listener_Node *node = dir_lookup.entry->listener_node; + ++node->listener.user_count; + + lookup.entry->hash = hash; + lookup.entry->file = file; + lookup.entry->dir = node->listener.dir; + lookup.entry->change_pos = -1; + ++tables->tracked_count; + + *index = hash; + internal_set_time(time, info); + } + else{ + result = FileTrack_FileAlreadyTracked; + } + } + else{ + result = FileTrack_OutOfTableMemory; + } + } + } + else{ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileSystemError; + } + + if (result != FileTrack_Good && dir != 0){ + CloseHandle(dir); + } + } + else{ + result = FileTrack_FileSystemError; + } + + if (result != FileTrack_Good){ + CloseHandle(file); + } + } + else{ + result = FileTrack_FileNotFound; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +internal_get_tracked_file_index(File_Track_Tables *tables, + char *name, + File_Index *index, + File_Track_Entry **entry){ + File_Track_Result result = FileTrack_Good; + + HANDLE file = CreateFile( + name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); + + if (file != INVALID_HANDLE_VALUE){ + BY_HANDLE_FILE_INFORMATION info = {0}; + if (GetFileInformationByHandle(file, &info)){ + File_Index hash; + hash.id[0] = info.nFileIndexLow; + hash.id[1] = info.nFileIndexHigh; + hash.id[2] = info.dwVolumeSerialNumber; + hash.id[3] = 0; + + File_Lookup_Result lookup = tracking_system_lookup_entry(tables, hash); + if (!entry_is_available(lookup.entry)){ + *index = hash; + *entry = lookup.entry; + } + else{ + result = FileTrack_FileNotTracked; + } + } + else{ + result = FileTrack_FileSystemError; + } + CloseHandle(file); + } + else{ + result = FileTrack_FileNotFound; + } + + return(result); +} + +File_Track_Result +get_tracked_file_index(File_Track_System *system, + char *name, + File_Index *index){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + File_Track_Tables *tables = 0; + File_Track_Entry *entry = 0; + + EnterCriticalSection(&vars->table_lock); + tables = to_tables(vars); + result = internal_get_tracked_file_index(tables, name, index, &entry); + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +stop_tracking_file(File_Track_System *system, File_Index index){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + File_Track_Entry *entry = get_file_entry(tables, index); + + if (entry){ + HANDLE dir = entry->dir; + if (dir != entry->file){ + BY_HANDLE_FILE_INFORMATION dir_info = {0}; + if (GetFileInformationByHandle(dir, &dir_info)){ + File_Index dir_hash = internal_get_file_index(dir_info); + + File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash); + + Assert(!entry_is_available(dir_lookup.entry)); + Directory_Listener_Node *node = dir_lookup.entry->listener_node; + --node->listener.user_count; + + if (node->listener.user_count == 0){ + insert_node(&vars->free_sentinel, &node->node); + CloseHandle(entry->dir); + internal_free_slot(tables, dir_lookup.entry); + --tables->tracked_dir_count; + } + + CloseHandle(entry->file); + internal_free_slot(tables, entry); + } + else{ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileNotTracked; + } + } + else{ + result = FileTrack_FileNotTracked; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +count_tracked_files(File_Track_System *system, int32_t *count){ + File_Track_Result result = FileTrack_Good; + File_Track_Tables *tables = to_tables(to_vars_(system)); + *count = tables->tracked_count - tables->tracked_dir_count; + return(result); +} + +File_Track_Result +get_tracked_file_time(File_Track_System *system, File_Index index, File_Time *time){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + File_Track_Entry *entry = get_file_entry(tables, index); + + if (entry){ + BY_HANDLE_FILE_INFORMATION info = {0}; + if (GetFileInformationByHandle(entry->file, &info)){ + internal_set_time(time, info); + } + else{ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileNotTracked; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +move_track_system(File_Track_System *system, void *mem, int32_t size){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *original_tables = to_tables(vars); + + if (original_tables->size < size){ + File_Track_Tables *tables = (File_Track_Tables*)mem; + + // NOTE(allen): Initialize main data tables + { + tables->size = size; + + int32_t likely_entry_size = FILE_ENTRY_COST; + int32_t max_number_of_entries = (size - sizeof(*tables)) / likely_entry_size; + + tables->change_queue = sizeof(*tables); + tables->file_table = tables->change_queue + + sizeof(File_Change_Record)*max_number_of_entries; + tables->max = max_number_of_entries; + } + + if (tables->max > original_tables->max){ + uint32_t original_max = original_tables->max; + + uint32_t change_shift = 0; + uint32_t change_modulo = original_max; + + // NOTE(allen): Copy the original change queue + { + uint32_t start_pos = original_tables->change_read_pos; + uint32_t end_pos = original_tables->change_write_pos; + + uint32_t changes_count = 0; + if (original_tables->change_write_pos < original_tables->change_read_pos){ + changes_count = original_max + end_pos - start_pos; + } + else{ + changes_count = end_pos - start_pos; + } + + change_shift = original_max - start_pos; + + File_Change_Record *src = (File_Change_Record*) + to_ptr(original_tables, original_tables->change_queue); + + File_Change_Record *dst = (File_Change_Record*) + to_ptr(tables, tables->change_queue); + + for (uint32_t i_dst = 0, i_src = start_pos; + i_dst < changes_count; + ++i_dst, i_src = (i_src+1)%original_max){ + dst[i_dst] = src[i_src]; + } + + tables->change_read_pos = 0; + tables->change_write_pos = changes_count; + } + + // NOTE(allen): Rehash the tracking table + { + File_Track_Entry *entries = (File_Track_Entry*) + to_ptr(original_tables, original_tables->file_table); + + for (uint32_t index = 0; + index < original_max; + ++index){ + File_Track_Entry *entry = entries + index; + if (!entry_is_available(entry)){ + File_Index hash = entry->hash; + File_Lookup_Result lookup = + tracking_system_lookup_entry(tables, hash); + Assert(entry_is_available(lookup.entry)); + + *lookup.entry = *entry; + + if (lookup.entry->file != lookup.entry->dir && + lookup.entry->change_pos != -1){ + lookup.entry->change_pos = + (lookup.entry->change_pos+change_shift)%change_modulo; + } + } + } + + tables->tracked_count = original_tables->tracked_count; + tables->tracked_dir_count = original_tables->tracked_dir_count; + } + + // NOTE(allen): Update to the new table + vars->tables = mem; + } + else{ + result = FileTrack_MemoryTooSmall; + } + } + else{ + result = FileTrack_MemoryTooSmall; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + if (sizeof(Directory_Listener_Node) <= size){ + Directory_Listener_Node *listener = (Directory_Listener_Node*)mem; + int32_t count = size / sizeof(Directory_Listener_Node); + for (int32_t i = 0; i < count; ++i, ++listener){ + insert_node(&vars->free_sentinel, &listener->node); + } + } + else{ + result = FileTrack_MemoryTooSmall; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +get_change_event(File_Track_System *system, File_Index *index){ + File_Track_Result result = FileTrack_NoMoreEvents; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + uint32_t write_pos = tables->change_write_pos; + uint32_t read_pos = tables->change_read_pos; + uint32_t max = tables->max; + + File_Change_Record *change_queue = + (File_Change_Record*)to_ptr(tables, tables->change_queue); + + while (read_pos != write_pos){ + File_Change_Record *record = change_queue + read_pos; + + read_pos = (read_pos + 1) % max; + + if (record->still_active){ + File_Lookup_Result lookup = tracking_system_lookup_entry(tables, record->index); + if (!entry_is_available(lookup.entry)){ + lookup.entry->change_pos = -1; + *index = record->index; + result = FileTrack_Good; + } + break; + } + } + + tables->change_write_pos = write_pos; + tables->change_read_pos = read_pos; + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +get_tracked_file_size(File_Track_System *system, File_Index index, uint32_t *size){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index); + if (!entry_is_available(lookup.entry)){ + DWORD lo, hi; + lo = GetFileSize(lookup.entry->file, &hi); + Assert(hi == 0); + *size = lo; + } + else{ + result = FileTrack_FileNotTracked; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +get_tracked_file_data(File_Track_System *system, File_Index index, void *mem, uint32_t size){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index); + if (!entry_is_available(lookup.entry)){ + DWORD read_size = 0; + if (ReadFile(lookup.entry->file, mem, size, &read_size, 0)){ + if (read_size != size){ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileNotTracked; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +rewrite_tracked_file(File_Track_System *system, File_Index index, + void *data, int32_t size, File_Time *time){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + EnterCriticalSection(&vars->table_lock); + + File_Track_Tables *tables = to_tables(vars); + + File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index); + if (!entry_is_available(lookup.entry)){ + DWORD written = 0; + + lookup.entry->skip_change = 1; + SetFilePointer(lookup.entry->file, 0, 0, FILE_BEGIN); + if (WriteFile(lookup.entry->file, data, size, &written, 0)){ + FILETIME file_time; + SYSTEMTIME system_time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + SetFileTime(lookup.entry->file, 0, 0, &file_time); + + FlushFileBuffers(lookup.entry->file); + } + else{ + result = FileTrack_FileSystemError; + } + } + else{ + result = FileTrack_FileNotTracked; + } + + LeaveCriticalSection(&vars->table_lock); + + return(result); +} + +File_Track_Result +shut_down_track_system(File_Track_System *system){ + File_Track_Result result = FileTrack_Good; + File_Track_Vars *vars = to_vars_(system); + + File_Track_Tables *tables = to_tables(vars); + + File_Track_Entry *entries = (File_Track_Entry*)to_ptr(tables, tables->file_table); + + uint32_t index = 0; + uint32_t max = tables->max; + + DWORD win32_result = 0; + + for (; index < max; ++index){ + File_Track_Entry *entry = entries + index; + if (!entry_is_available(entry)){ + if (!CloseHandle(entry->file)){ + win32_result = 1; + } + } + } + + if (!CloseHandle(vars->iocp)){ + win32_result = 1; + } + TerminateThread(vars->thread, 0); + if (!CloseHandle(vars->thread)){ + win32_result = 1; + } + + if (win32_result){ + result = FileTrack_FileSystemError; + } + + DeleteCriticalSection(&vars->table_lock); + + return(result); +} + +// BOTTOM diff --git a/filetrack/build.bat b/filetrack/build.bat new file mode 100644 index 00000000..fc1cfddc --- /dev/null +++ b/filetrack/build.bat @@ -0,0 +1,10 @@ +@echo off + +set WARNINGOPS=/W4 /wd4310 /wd4100 /wd4201 /wd4505 /wd4996 /wd4127 /wd4510 /wd4512 /wd4610 /wd4390 /WX +set WARNINGOPS=%WARNINGOPS% /GR- /EHa- /nologo /FC +set WIN_LIBS=user32.lib winmm.lib gdi32.lib + +pushd w:\filetrack\build +cl %WARNINGOPS% ..\code\filetrack_test.c /Fefile_rewriter /Zi %* +cl %WARNINGOPS% ..\code\filetrack_main.c /Fefiletrack /Zi %* +popd \ No newline at end of file diff --git a/filetrack/filetrack_main.c b/filetrack/filetrack_main.c new file mode 100644 index 00000000..fbe70501 --- /dev/null +++ b/filetrack/filetrack_main.c @@ -0,0 +1,436 @@ +/* + +Copy Right FourTech LLC, 2016 +All Rights Are Reserved + +A test bed for a cross platform file tracking reliability layer. +Developed for the use cases in 4coder, but I anticipate that this +will be a general problem for me. - Allen Webster + +Created on: 20.07.2016 + +*/ + +// TOP + +#define FILE_TRACK_MAIN + +#include "4tech_file_track.h" +#include "4tech_file_track_win32.c" + +#include "filetrack_test.c" + +#include +#include +#include +#include +#include + +#define FILE_TRACK_TEST_DIR1 "w:/filetrack/data/" +#define FILE_TRACK_TEST_DIR2 "w:/filetrack/data2/" + +#define FAKE_TRACK_TEST_DIR "w:/filetrack/data1000/" + +#define ALT_NAME_TEST_DIR1 "c:/work/filetrack/data/" +#define ALT_NAME_TEST_DIR2 "c:/work/filetrack/data2/" + +static char * test_files[] = { + FILE_TRACK_TEST_DIR1"autotab.cpp", + FILE_TRACK_TEST_DIR1"basic.cpp", + FILE_TRACK_TEST_DIR1"basic.txt", + FILE_TRACK_TEST_DIR1"cleanme.cpp", + FILE_TRACK_TEST_DIR1"emptyfile.txt", + FILE_TRACK_TEST_DIR1"lexer_test.cpp", + FILE_TRACK_TEST_DIR1"lexer_test2.cpp", + FILE_TRACK_TEST_DIR1"lexer_test3.cpp", + FILE_TRACK_TEST_DIR1"saveas.txt", + FILE_TRACK_TEST_DIR1"test_large.cpp", + + FILE_TRACK_TEST_DIR2"autotab.cpp", + FILE_TRACK_TEST_DIR2"basic.cpp", + FILE_TRACK_TEST_DIR2"basic.txt", + FILE_TRACK_TEST_DIR2"cleanme.cpp", + FILE_TRACK_TEST_DIR2"emptyfile.txt", + FILE_TRACK_TEST_DIR2"lexer_test.cpp", + FILE_TRACK_TEST_DIR2"lexer_test2.cpp", + FILE_TRACK_TEST_DIR2"lexer_test3.cpp", + FILE_TRACK_TEST_DIR2"saveas.txt", + FILE_TRACK_TEST_DIR2"test_large.cpp", +}; + +static char * test_alt_files[] = { + ALT_NAME_TEST_DIR1"autotab.cpp", + ALT_NAME_TEST_DIR1"basic.cpp", + ALT_NAME_TEST_DIR1"basic.txt", + ALT_NAME_TEST_DIR1"cleanme.cpp", + ALT_NAME_TEST_DIR1"emptyfile.txt", + ALT_NAME_TEST_DIR1"lexer_test.cpp", + ALT_NAME_TEST_DIR1"lexer_test2.cpp", + ALT_NAME_TEST_DIR1"lexer_test3.cpp", + ALT_NAME_TEST_DIR1"saveas.txt", + ALT_NAME_TEST_DIR1"test_large.cpp", + + ALT_NAME_TEST_DIR2"autotab.cpp", + ALT_NAME_TEST_DIR2"basic.cpp", + ALT_NAME_TEST_DIR2"basic.txt", + ALT_NAME_TEST_DIR2"cleanme.cpp", + ALT_NAME_TEST_DIR2"emptyfile.txt", + ALT_NAME_TEST_DIR2"lexer_test.cpp", + ALT_NAME_TEST_DIR2"lexer_test2.cpp", + ALT_NAME_TEST_DIR2"lexer_test3.cpp", + ALT_NAME_TEST_DIR2"saveas.txt", + ALT_NAME_TEST_DIR2"test_large.cpp", +}; + +static char * fake_files[] = { + FAKE_TRACK_TEST_DIR"autotab.cpp", + FAKE_TRACK_TEST_DIR"basic.cpp", + FAKE_TRACK_TEST_DIR"basic.txt", + FAKE_TRACK_TEST_DIR"cleanme.cpp", + FAKE_TRACK_TEST_DIR"emptyfile.txt", + FAKE_TRACK_TEST_DIR"lexer_test.cpp", + FAKE_TRACK_TEST_DIR"lexer_test2.cpp", + FAKE_TRACK_TEST_DIR"lexer_test3.cpp", + FAKE_TRACK_TEST_DIR"saveas.txt", + FAKE_TRACK_TEST_DIR"test_large.cpp", +}; + +#define ArrayCount(a) ((sizeof(a))/(sizeof(*a))) + +typedef struct{ + File_Index unique_file_index; + File_Time time; +} MyFileThing; + +void test_body_A(int32_t size1, int32_t size2){ + void *mem1 = malloc(size1); + void *mem2 = malloc(size2); + memset(mem1, 0, size1); + + File_Track_System track = {0}; + int32_t result = init_track_system(&track, + mem1, size1, + mem2, size2); + assert(result == FileTrack_Good); + + MyFileThing my_file_things[1000]; + memset(my_file_things, 0, sizeof(my_file_things)); + + // NOTE(allen): track in all the test files + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + char *filename = test_files[i]; + + File_Index new_file = zero_file_index(); + File_Time new_time = 0; + int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time); + while (result != FileTrack_Good){ + + switch (result){ + case FileTrack_OutOfTableMemory: + { + int32_t new_mem_size = size1*2; + void *new_mem = malloc(new_mem_size); + + memset(new_mem, 0, new_mem_size); + move_track_system(&track, new_mem, new_mem_size); + + free(mem1); + size1 = new_mem_size; + mem1 = new_mem; + }break; + + case FileTrack_OutOfListenerMemory: + { + size2 *= 2; + void *new_mem = malloc(size2); + memset(new_mem, 0, size2); + expand_track_system_listeners(&track, new_mem, size2); + }break; + + default: + { + Assert(result == FileTrack_Good); + }break; + } + + result = begin_tracking_file(&track, filename, &new_file, &new_time); + } + + my_file_things[i].unique_file_index = new_file; + my_file_things[i].time = new_time; + } + + // NOTE(allen): track in fake directories + for (int32_t i = 0; + i < ArrayCount(fake_files); + ++i){ + File_Index new_file = zero_file_index(); + File_Time new_time = 0; + + char *filename = fake_files[i]; + + int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time); + assert(result == FileTrack_FileNotFound); + } + + // NOTE(allen): track in already tracked files + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Index new_file = zero_file_index(); + File_Time new_time = 0; + + char *filename = test_files[i]; + + int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time); + assert(result == FileTrack_FileAlreadyTracked); + } + + // NOTE(allen): track in already tracked files via alt-names + for (int32_t i = 0; + i < ArrayCount(test_alt_files); + ++i){ + File_Index new_file = zero_file_index(); + File_Time new_time = 0; + + char *filename = test_alt_files[i]; + + int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time); + assert(result == FileTrack_FileAlreadyTracked); + } + + // NOTE(allen): each file is still up to date + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Time time = 0; + File_Index index = my_file_things[i].unique_file_index; + + get_tracked_file_time(&track, index, &time); + + assert(time == my_file_things[i].time); + } + + // NOTE(allen): can still get index from file name + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Index index = my_file_things[i].unique_file_index; + + File_Index result_index1 = zero_file_index(); + char *filename1 = test_files[i]; + get_tracked_file_index(&track, filename1, &result_index1); + + File_Index result_index2 = zero_file_index(); + char *filename2 = test_alt_files[i]; + get_tracked_file_index(&track, filename2, &result_index2); + + assert(file_index_eq(result_index1, index)); + assert(file_index_eq(result_index2, index)); + } + + // NOTE(allen): rewrite all of the files + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + char *filename = test_files[i]; + + test_rewrite_file_in_child_proc(filename); + } + + // NOTE(allen): each file is behind + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Time time = 0; + File_Index index = my_file_things[i].unique_file_index; + + get_tracked_file_time(&track, index, &time); + + assert(my_file_things[i].time < time); + } + + // NOTE(allen): poll the tracking system for changed files + for (;;){ + File_Index index = zero_file_index(); + File_Time time = 0; + + int32_t result = get_change_event(&track, &index); + + if (result == FileTrack_NoMoreEvents){ + break; + } + + result = get_tracked_file_time(&track, index, &time); + + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Index my_index = my_file_things[i].unique_file_index; + if (file_index_eq(my_index, index)){ + my_file_things[i].time = time; + break; + } + } + } + + // NOTE(allen): each file is still up to date (episode 2) + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Time time = 0; + File_Index index = my_file_things[i].unique_file_index; + + get_tracked_file_time(&track, index, &time); + + assert(time == my_file_things[i].time); + } + + // NOTE(allen): rewrite each file myself + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Index index = my_file_things[i].unique_file_index; + File_Time time = test_rewrite_file(&track, index); + + my_file_things[i].time = time; + } + + // NOTE(allen): check there are no changed file events + { + File_Index index = zero_file_index(); + + int32_t result = get_change_event(&track, &index); + assert(result == FileTrack_NoMoreEvents); + } + + // NOTE(allen): rewrite half of the files twice + int32_t mid_point = ArrayCount(test_files) / 2; + for (int32_t i = 0; + i < mid_point; + ++i){ + char *filename = test_files[i]; + + test_rewrite_file_in_child_proc(filename); + } + + for (int32_t i = 0; + i < mid_point; + ++i){ + char *filename = test_files[i]; + + test_rewrite_file_in_child_proc(filename); + } + + // NOTE(allen): check number of events equals mid_point + int32_t count = 0; + for (;;){ + File_Index index = zero_file_index(); + File_Time time = 0; + + int32_t result = get_change_event(&track, &index); + + if (result == FileTrack_NoMoreEvents){ + break; + } + + result = get_tracked_file_time(&track, index, &time); + + ++count; + + for (int32_t i = 0; + i < ArrayCount(test_files); + ++i){ + File_Index my_index = my_file_things[i].unique_file_index; + if (file_index_eq(my_index, index)){ + my_file_things[i].time = time; + break; + } + } + } + assert(count == mid_point); + + // NOTE(allen): untrack half of the files + for (int32_t i = 0; + i < mid_point; + ++i){ + File_Index stop_file = my_file_things[i].unique_file_index; + stop_tracking_file(&track, stop_file); + } + + // NOTE(allen): untrack the same files again + for (int32_t i = 0; + i < mid_point; + ++i){ + File_Index stop_file = my_file_things[i].unique_file_index; + int32_t result = stop_tracking_file(&track, stop_file); + assert(result == FileTrack_FileNotTracked); + } + + // NOTE(allen): make sure the number of remaining files is correct + { + int32_t track_count = 0; + count_tracked_files(&track, &track_count); + assert(track_count == (ArrayCount(test_files) - mid_point)); + } + + // NOTE(allen): untrack the rest of the files + for (int32_t i = mid_point; + i < ArrayCount(test_files); + ++i){ + File_Index stop_file = my_file_things[i].unique_file_index; + stop_tracking_file(&track, stop_file); + } + + // NOTE(allen): make sure the system is empty + { + int32_t track_count = 0; + count_tracked_files(&track, &track_count); + assert(track_count == 0); + } + + // NOTE(allen): finish using the track system + { + int32_t result = shut_down_track_system(&track); + assert(result == FileTrack_Good); + } +} + + +// NOTE(allen): test basic tracking logic +void test_1(void){ + int32_t size1 = (16 << 10); + int32_t size2 = (16 << 10); + test_body_A(size1, size2); +} + +// NOTE(allen): test memory expansion system for tables +void test_2(void){ + int32_t size1 = (1 << 10); + int32_t size2 = (16 << 10); + test_body_A(size1, size2); +} + +// NOTE(allen): test memory expansion system for listening nodes +void test_3(void){ + int32_t size1 = (16 << 10); + int32_t size2 = (5 << 10); + test_body_A(size1, size2); +} + +// NOTE(allen): test both memory expansion systems +void test_4(void){ + int32_t size1 = (1 << 10); + int32_t size2 = (5 << 10); + test_body_A(size1, size2); +} + +int main(int argc, char **argv){ + test_4(); + return(0); +} + +// BOTTOM + diff --git a/filetrack/filetrack_test.c b/filetrack/filetrack_test.c new file mode 100644 index 00000000..92b96f0b --- /dev/null +++ b/filetrack/filetrack_test.c @@ -0,0 +1,114 @@ +/* + +Copy Right FourTech LLC, 2016 +All Rights Are Reserved + +Helpers for the filetrack_main.c test bed. + +Created on: 20.07.2016 + +*/ + +// TOP + +#define FILE_REWRITER "w:/filetrack/build/file_rewriter" + +#include +#include +#include +#include + +static void +rewrite(char *buffer, int32_t size){ + for (int32_t i = 0; + i < size; + ++i){ + if (buffer[i] >= 'a' && buffer[i] < 'z'){ + ++buffer[i]; + } + else if (buffer[i] == 'z'){ + buffer[i] = 'a'; + } + } +} + +static void +append(char *buffer, int32_t *pos, char *src){ + int32_t i = *pos; + src -= i; + for (; src[i]; ++i){ + buffer[i] = src[i]; + } + *pos = i; +} + +static void +test_rewrite_file_in_child_proc(char *filename){ + char space[2048]; + int32_t pos = 0; + + append(space, &pos, FILE_REWRITER" "); + append(space, &pos, filename); + space[pos] = 0; + + int32_t result = system(space); + assert(result == 0); +} + +#ifndef FILE_TRACK_MAIN + +int +main(int argc, char **argv){ + if (argc == 2){ + char *filename = argv[1]; + + char *mem = 0; + int32_t size = 0; + + FILE *file = fopen(filename, "rb"); + assert(file); + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + mem = (char*)malloc(size+1); + fread(mem, 1, size, file); + fclose(file); + + rewrite(mem, size); + + file = fopen(filename, "wb"); + assert(file); + fwrite(mem, 1, size, file); + fclose(file); + } + return(0); +} + +#else + +static File_Time +test_rewrite_file(File_Track_System *system, File_Index index){ + char *mem = 0; + uint32_t size = 0; + int32_t result = 0; + + result = get_tracked_file_size(system, index, &size); + assert(result == FileTrack_Good); + mem = (char*)malloc(size+1); + result = get_tracked_file_data(system, index, mem, size); + assert(result == FileTrack_Good); + + rewrite(mem, size); + + File_Time time = 0; + rewrite_tracked_file(system, index, mem, size, &time); + + free(mem); + + return(time); +} + +#endif + +// BOTTOM + diff --git a/thing.txt b/thing.txt new file mode 100644 index 00000000..3fa82cdb --- /dev/null +++ b/thing.txt @@ -0,0 +1,21 @@ + +// + +Theme_Color ThemeColors[] = { + {Stag_Bar, 0x9a99e7}, + {Stag_Bar_Active, 0x9a99e7}, + {Stag_Margin, 0x606590}, + {Stag_Margin_Hover, 0x9a99e7}, + {Stag_Margin_Active, 0x9a99e7}, + {Stag_Comment, 0x505f89}, + {Stag_Keyword, 0xaa8da7}, + {Stag_Default, 0xffffff}, + {Stag_Cursor, 0xd96e26}, + {Stag_Int_Constant, 0x9a99e7}, + {Stag_Float_Constant, 0x9a99e7}, + {Stag_Bool_Constant, 0x9a99e7}, + {Stag_Str_Constant, 0x9a99e7}, + {Stag_Char_Constant, 0x9a99e7}, + {Stag_Preproc, 0x606590}, + {Stag_Include, 0x9a99e7}, +}; diff --git a/win32_4ed.cpp b/win32_4ed.cpp index 40b15fc7..30d98f53 100644 --- a/win32_4ed.cpp +++ b/win32_4ed.cpp @@ -352,202 +352,6 @@ system_signal_cv(i32 crit_id, i32 cv_id){ WakeConditionVariable(win32vars.condition_vars + cv_id); } -#if 0 -internal DWORD -JobThreadProc(LPVOID lpParameter){ - Thread_Context *thread = (Thread_Context*)lpParameter; - Work_Queue *queue = win32vars.queues + thread->group_id; - Thread_Group *group = win32vars.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 = win32vars.thread_memory + thread_index; - - if (thread_memory->size == 0){ - i32 new_size = Kbytes(64); - thread_memory->data = Win32GetMemory(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){ - // NOTE(allen): Previously I was wrapping by the job wrap then - // wrapping by the queue wrap. That was super stupid what was that? - // Now it just wraps by the queue wrap. - u32 next_read_index = (read_index + 1) % QUEUE_WRAP; - u32 safe_read_index = - InterlockedCompareExchange(&queue->read_position, - next_read_index, 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 = - InterlockedCompareExchange(&full_job->running_thread, - thread->id, THREAD_NOT_ASSIGNED); - - if (safe_running_thread == THREAD_NOT_ASSIGNED){ - thread->job_id = full_job->id; - thread->running = 1; - - full_job->job.callback(&win32vars.system, - thread, thread_memory, full_job->job.data); - PostMessage(win32vars.window_handle, WM_4coder_ANIMATE, 0, 0); - full_job->running_thread = 0; - thread->running = 0; - - system_acquire_lock(cancel_lock); - if (thread->cancel){ - thread->cancel = 0; - system_signal_cv(cancel_lock, cancel_cv); - } - system_release_lock(cancel_lock); - } - } - } - else{ - WaitForSingleObject(Win32Handle(queue->semaphore), INFINITE); - } - } -} - -internal -Sys_Post_Job_Sig(system_post_job){ - Work_Queue *queue = win32vars.queues + group_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 = - InterlockedCompareExchange(&queue->write_position, - next_write_index, write_index); - if (safe_write_index == write_index){ - result = write_index; - queue->jobs[write_index].job = job; - queue->jobs[write_index].running_thread = THREAD_NOT_ASSIGNED; - queue->jobs[write_index].id = result; - success = 1; - } - } - - ReleaseSemaphore(Win32Handle(queue->semaphore), 1, 0); - - return result; -} - -// NOTE(allen): New job cancelling system: -// -// Jobs are expected to periodically check their cancelation -// state, especially if they are taking a while. -// -// When the main thread asks to cancel a job it sets the cancel -// state and does not resume until the thread running the job -// signals that it is okay. -// -// Don't hold the frame lock while sleeping, as this can dead-lock -// the job thread and the main thread, and since main is sleeping -// they won't collide anyway. -internal -Sys_Cancel_Job_Sig(system_cancel_job){ - Work_Queue *queue = win32vars.queues + group_id; - Thread_Group *group = win32vars.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 = - InterlockedCompareExchange(&full_job->running_thread, - 0, THREAD_NOT_ASSIGNED); - - 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{ - system_wait_cv(cancel_lock, cancel_cv); - }while (thread->cancel == 1); - system_acquire_lock(FRAME_LOCK); - - system_release_lock(cancel_lock); - } -} - -internal -Sys_Check_Cancel_Sig(system_check_cancel){ - b32 result = 0; - - Thread_Group *group = win32vars.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); -} - -#if FRED_INTERNAL -internal void -INTERNAL_get_thread_states(Thread_Group_ID id, bool8 *running, i32 *pending){ - Work_Queue *queue = win32vars.queues + id; - u32 write = queue->write_position; - u32 read = queue->read_position; - if (write < read) write += QUEUE_WRAP; - *pending = (i32)(write - read); - - Thread_Group *group = win32vars.groups + id; - for (i32 i = 0; i < group->count; ++i){ - running[i] = (group->threads[i].running != 0); - } -} -#endif - -#else - internal DWORD JobThreadProc(LPVOID lpParameter){ Thread_Context *thread = (Thread_Context*)lpParameter; @@ -854,8 +658,6 @@ INTERNAL_get_thread_states(Thread_Group_ID id, bool8 *running, i32 *pending){ } #endif -#endif - // // Coroutines