file track system is ready for porting

master
Allen Webster 2016-08-27 13:48:10 -04:00
parent 44756b2d5c
commit f7a2affb9f
8 changed files with 402 additions and 443 deletions

View File

@ -1930,9 +1930,10 @@ CUSTOM_COMMAND_SIG(open_all_code){
String extension = make_string(info->filename, info->filename_len, info->filename_len+1); String extension = make_string(info->filename, info->filename_len, info->filename_len+1);
extension = file_extension(extension); extension = file_extension(extension);
if (match(extension, make_lit_string("cpp")) || if (match(extension, make_lit_string("cpp")) ||
match(extension, make_lit_string("hpp")) || match(extension, make_lit_string("hpp")) ||
match(extension, make_lit_string("c")) || match(extension, make_lit_string("c")) ||
match(extension, make_lit_string("h"))){ match(extension, make_lit_string("h")) ||
match(extension, make_lit_string("cc"))){
// NOTE(allen): There's no way in the 4coder API to use relative // NOTE(allen): There's no way in the 4coder API to use relative
// paths at the moment, so everything should be full paths. Which is // paths at the moment, so everything should be full paths. Which is
// managable. Here simply set the dir string size back to where it // managable. Here simply set the dir string size back to where it

View File

@ -1094,7 +1094,7 @@ Kill_Buffer(Application_Links *app, Buffer_Identifier buffer, View_ID view_id, B
DOC_PARAM(buffer, The buffer parameter specifies the buffer to try to kill.) DOC_PARAM(buffer, The buffer parameter specifies the buffer to try to kill.)
DOC_PARAM(view_id, The view_id parameter specifies the view that will contain the "are you sure" dialogue if the buffer is dirty.) DOC_PARAM(view_id, The view_id parameter specifies the view that will contain the "are you sure" dialogue if the buffer is dirty.)
DOC_PARAM(flags, The flags parameter specifies behaviors for the buffer kill.) DOC_PARAM(flags, The flags parameter specifies behaviors for the buffer kill.)
DOC_RETURN(This call returns non-zero on success.) DOC_RETURN(This call returns non-zero if the buffer is killed.)
DOC DOC
( (
Tries to kill the idenfied buffer. If the buffer is dirty and the "are you sure" Tries to kill the idenfied buffer. If the buffer is dirty and the "are you sure"
@ -1118,12 +1118,21 @@ DOC_SEE(Buffer_Identifier)
kill_file(system, models, file); kill_file(system, models, file);
} }
else{ else{
if (vptr){ Try_Kill_Result kill_result = interactive_try_kill_file(system, models, file);
result = true; if (kill_result == TryKill_NeedDialogue){
interactive_try_kill_file(system, models, vptr, file); if (vptr){
interactive_begin_sure_to_kill(system, vptr, file);
}
else{
#define MESSAGE "CUSTOM WARNING: the buffer is dirty and no view was specified for a dialogue.\n"
app->print_message(app, literal(MESSAGE));
#undef MESSAGE
}
} }
else{ else{
app->print_message(app, literal("CUSTOM WARNING: the buffer is dirty and no view was specified for a dialogue.")); if (kill_result == TryKill_Success){
result = true;
}
} }
} }
} }

View File

@ -3312,24 +3312,45 @@ save_file_by_name(System_Functions *system, Models *models, String name){
} }
} }
internal b32 internal void
interactive_try_kill_file(System_Functions *system, Models *models, View *view, Editing_File *file){ interactive_begin_sure_to_kill(System_Functions *system, View *view, Editing_File *file){
b32 kill_dialogue = false; view_show_interactive(system, view,
IAct_Sure_To_Kill, IInt_Sure_To_Kill,
make_lit_string("Are you sure?"));
copy(&view->dest, file->name.live_name);
}
enum Try_Kill_Result{
TryKill_CannotKill,
TryKill_NeedDialogue,
TryKill_Success
};
internal Try_Kill_Result
interactive_try_kill_file(System_Functions *system, Models *models, Editing_File *file){
Try_Kill_Result result = TryKill_CannotKill;
if (!file->settings.never_kill){ if (!file->settings.never_kill){
if (buffer_needs_save(file)){ if (buffer_needs_save(file)){
view_show_interactive(system, view, result = TryKill_NeedDialogue;
IAct_Sure_To_Kill, IInt_Sure_To_Kill,
make_lit_string("Are you sure?"));
copy(&view->dest, file->name.live_name);
kill_dialogue = true;
} }
else{ else{
kill_file(system, models, file); kill_file(system, models, file);
result = TryKill_Success;
} }
} }
return(kill_dialogue); return(result);
}
internal b32
interactive_try_kill_file(System_Functions *system, Models *models, View *view, Editing_File *file){
Try_Kill_Result kill_result = interactive_try_kill_file(system, models, file);
b32 result = (kill_result == TryKill_NeedDialogue);
if (result){
interactive_begin_sure_to_kill(system, view, file);
}
return(result);
} }
internal b32 internal b32

View File

@ -3,4 +3,4 @@
REM "build_exp.bat" /O2 REM "build_exp.bat" /O2
"build_all.bat" /DFRED_SUPER /DFRED_INTERNAL /Zi "build_all.bat" /DFRED_SUPER /DFRED_INTERNAL /Zi
REM "build_all.bat" /DFRED_INTERNAL /Zi REM "build_all.bat" /DFRED_INTERNAL /Zi
REM "build_all.bat" /O2 /Zi REM "build_all.bat" /DFRED_SUPER /O2 /Zi

View File

@ -26,16 +26,16 @@ popd
pushd ..\build pushd ..\build
REM call "%CODE_DIR%\buildsuper.bat" ..\code\4coder_default_bindings.cpp REM call "%CODE_DIR%\buildsuper.bat" ..\code\4coder_default_bindings.cpp
call "%CODE_DIR%\buildsuper.bat" ..\code\internal_4coder_tests.cpp REM call "%CODE_DIR%\buildsuper.bat" ..\code\internal_4coder_tests.cpp
REM call "%CODE_DIR%\buildsuper.bat" ..\code\power\4coder_casey.cpp REM call "%CODE_DIR%\buildsuper.bat" ..\code\power\4coder_casey.cpp
REM call "%CODE_DIR%\buildsuper.bat" ..\4vim\4coder_chronal.cpp REM call "%CODE_DIR%\buildsuper.bat" ..\4vim\4coder_chronal.cpp
if %ERRORLEVEL% neq 0 (set FirstError=1) if %ERRORLEVEL% neq 0 (set FirstError=1)
set EXPORTS=/EXPORT:app_get_functions set EXPORTS=/EXPORT:app_get_functions
cl %OPTS% %INCLUDES% %DEFINES% %CODE_DIR%\4ed_app_target.cpp %* /Fe4ed_app /LD /link /INCREMENTAL:NO /OPT:REF %EXPORTS% REM cl %OPTS% %INCLUDES% %DEFINES% %CODE_DIR%\4ed_app_target.cpp %* /Fe4ed_app /LD /link /DEBUG /INCREMENTAL:NO /OPT:REF %EXPORTS%
if %ERRORLEVEL% neq 0 (set FirstError=1) if %ERRORLEVEL% neq 0 (set FirstError=1)
cl %OPTS% %INCLUDES% %DEFINES% %CODE_DIR%\win32_4ed.cpp %LIBS% %ICON% %* /Fe4ed /link /NODEFAULTLIB:library cl %OPTS% %INCLUDES% %DEFINES% %CODE_DIR%\win32_4ed.cpp %LIBS% %ICON% %* /Fe4ed /link /DEBUG /NODEFAULTLIB:library
if %ERRORLEVEL% neq 0 (set FirstError=1) if %ERRORLEVEL% neq 0 (set FirstError=1)
call "print_size.bat" 4ed_app.dll call "print_size.bat" 4ed_app.dll
@ -46,5 +46,3 @@ popd
call "ctime" -end 4ed_data.ctm %FirstError% call "ctime" -end 4ed_data.ctm %FirstError%

View File

@ -0,0 +1,270 @@
/*
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: 27.08.2016
*/
// TOP
#ifndef Assert
# define Assert(c) do { if (!(c)) { *((int*)0) = 0xA11E; } } while (0)
#endif
#ifndef ZeroStruct
# define ZeroStruct(s) for (int32_t i = 0; i < sizeof(s); ++i) { ((char*)(&(s)))[i] = 0; }
#endif
typedef struct{
uint32_t id[4];
} File_Index;
typedef uint32_t rptr32;
#define to_ptr(b,p) ((void*)((char*)b + p))
#define to_rptr32(b,p) ((rptr32)((char*)(p) - (char*)(b)))
typedef struct {
File_Index hash;
uint32_t opaque[4];
} File_Track_Entry;
typedef struct {
int32_t size;
uint32_t tracked_count;
uint32_t max;
rptr32 file_table;
} File_Track_Tables;
typedef struct DLL_Node {
struct DLL_Node *next;
struct DLL_Node *prev;
} DLL_Node;
static File_Index
zero_file_index(){
File_Index a = {0};
return(a);
}
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
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]));
}
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);
}
#define FILE_ENTRY_COST (sizeof(File_Track_Entry))
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);
}
static File_Track_Entry*
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_Track_Entry* result = 0;
for (;;){
File_Track_Entry *entry = entries + index;
if (file_index_eq(entry->hash, key)){
result = entry;
break;
}
else if (file_hash_is_zero(entry->hash)){
if (result == 0){
result = entry;
}
break;
}
else if (file_hash_is_deleted(entry->hash)){
if (result == 0){
result = 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_Track_Entry *result = tracking_system_lookup_entry(tables, index);
if (result && file_index_eq(index, result->hash)){
entry = result;
}
return(entry);
}
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;
}
static int32_t
enough_memory_to_init_table(int32_t table_memory_size){
int32_t result = (sizeof(File_Track_Tables) + FILE_ENTRY_COST*8 <= table_memory_size);
return(result);
}
static void
init_table_memory(File_Track_Tables *tables, int32_t table_memory_size){
tables->size = table_memory_size;
tables->tracked_count = 0;
int32_t max_number_of_entries =
(table_memory_size - sizeof(*tables)) / FILE_ENTRY_COST;
tables->file_table = sizeof(*tables);
tables->max = max_number_of_entries;
}
static File_Track_Result
move_table_memory(File_Track_Tables *original_tables,
void *mem, int32_t size){
File_Track_Result result = FileTrack_Good;
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->file_table = sizeof(*tables);
tables->max = max_number_of_entries;
}
if (tables->max > original_tables->max){
uint32_t original_max = original_tables->max;
// 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_Track_Entry *lookup =
tracking_system_lookup_entry(tables, hash);
Assert(entry_is_available(lookup));
*lookup = *entry;
}
}
tables->tracked_count = original_tables->tracked_count;
}
}
else{
result = FileTrack_MemoryTooSmall;
}
}
else{
result = FileTrack_MemoryTooSmall;
}
return(result);
}
// BOTTOM

View File

@ -15,355 +15,61 @@ Created on: 20.07.2016
#include "4tech_file_track.h" #include "4tech_file_track.h"
#include "4tech_file_track_general.c"
#include <Windows.h> #include <Windows.h>
#ifndef Assert
# define Assert(c) do { if (!(c)) { *((int*)0) = 0xA11E; } } while (0)
#endif
#ifndef ZeroStruct
# define ZeroStruct(s) for (int32_t i = 0; i < sizeof(s); ++i) { ((char*)(&(s)))[i] = 0; }
#endif
#ifndef NotImplemented
# define NotImplemented Assert(!"not implemented")
#endif
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 uint32_t rptr32;
typedef struct DLL_Node {
struct DLL_Node *next;
struct DLL_Node *prev;
} DLL_Node;
typedef struct { typedef struct {
char result[2048];
OVERLAPPED overlapped; OVERLAPPED overlapped;
HANDLE dir; HANDLE dir;
int32_t user_count; int32_t user_count;
} Win32_Directory_Listener;
char result[2048];
} Directory_Listener;
typedef struct { typedef struct {
DLL_Node node; DLL_Node node;
Directory_Listener listener; Win32_Directory_Listener listener;
} Directory_Listener_Node; } Win32_Directory_Listener_Node;
typedef struct { typedef struct {
HANDLE iocp; HANDLE iocp;
CRITICAL_SECTION table_lock; CRITICAL_SECTION table_lock;
void *tables; void *tables;
DLL_Node free_sentinel; DLL_Node free_sentinel;
} Win32_File_Track_Vars;
} File_Track_Vars;
typedef struct { typedef struct {
int32_t size;
uint32_t tracked_count;
uint32_t max;
rptr32 file_table;
} File_Track_Tables;
typedef struct {
HANDLE dir;
File_Index hash; File_Index hash;
Directory_Listener_Node *listener_node; HANDLE dir;
} File_Track_Entry; Win32_Directory_Listener_Node *listener_node;
} Win32_File_Track_Entry;
#define FILE_ENTRY_COST (sizeof(File_Track_Entry)) #define to_vars(s) ((Win32_File_Track_Vars*)(s))
#define to_vars_(s) ((File_Track_Vars*)(s))
#define to_tables(v) ((File_Track_Tables*)(v->tables)) #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);
}
#if 0
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 = (File_Change_Record*)
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);
}
}
}
#endif
File_Track_Result File_Track_Result
init_track_system(File_Track_System *system, init_track_system(File_Track_System *system,
void *table_memory, int32_t table_memory_size, void *table_memory, int32_t table_memory_size,
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;
File_Track_Vars *vars = to_vars_(system); Win32_File_Track_Vars *vars = to_vars(system);
if (sizeof(File_Track_Tables) + FILE_ENTRY_COST*8 <= table_memory_size && Assert(sizeof(Win32_File_Track_Entry) <= sizeof(File_Track_Entry));
sizeof(Directory_Listener_Node) <= listener_memory_size){
vars->tables = table_memory; if (enough_memory_to_init_table(table_memory_size) &&
sizeof(Win32_Directory_Listener_Node) <= listener_memory_size){
File_Track_Tables *tables = to_tables(vars);
// NOTE(allen): Initialize main data tables // NOTE(allen): Initialize main data tables
{ vars->tables = table_memory;
tables->size = table_memory_size; File_Track_Tables *tables = to_tables(vars);
tables->tracked_count = 0; init_table_memory(tables, table_memory_size);
int32_t likely_entry_size = FILE_ENTRY_COST;
int32_t max_number_of_entries = (table_memory_size - sizeof(*tables)) / likely_entry_size;
tables->file_table = sizeof(*tables);
tables->max = max_number_of_entries;
}
// NOTE(allen): Initialize nodes of directory watching // NOTE(allen): Initialize nodes of directory watching
{ {
init_sentinel_node(&vars->free_sentinel); init_sentinel_node(&vars->free_sentinel);
Directory_Listener_Node *listener = (Directory_Listener_Node*)listener_memory; Win32_Directory_Listener_Node *listener = (Win32_Directory_Listener_Node*)listener_memory;
int32_t count = listener_memory_size / sizeof(Directory_Listener_Node); int32_t count = listener_memory_size / sizeof(Win32_Directory_Listener_Node);
for (int32_t i = 0; i < count; ++i, ++listener){ for (int32_t i = 0; i < count; ++i, ++listener){
insert_node(&vars->free_sentinel, &listener->node); insert_node(&vars->free_sentinel, &listener->node);
} }
@ -414,23 +120,10 @@ internal_get_file_index(BY_HANDLE_FILE_INFORMATION info){
return(hash); return(hash);
} }
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 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;
File_Track_Vars *vars = to_vars_(system); Win32_File_Track_Vars *vars = to_vars(system);
EnterCriticalSection(&vars->table_lock); EnterCriticalSection(&vars->table_lock);
{ {
@ -455,11 +148,12 @@ add_listener(File_Track_System *system, char *filename){
if (getinfo_result){ if (getinfo_result){
File_Index dir_hash = internal_get_file_index(dir_info); File_Index dir_hash = internal_get_file_index(dir_info);
File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash); File_Track_Entry *dir_lookup = tracking_system_lookup_entry(tables, dir_hash);
Win32_File_Track_Entry *win32_entry = (Win32_File_Track_Entry*)dir_lookup;
if (entry_is_available(dir_lookup.entry)){ if (entry_is_available(dir_lookup)){
if (tracking_system_has_space(tables, 1)){ if (tracking_system_has_space(tables, 1)){
Directory_Listener_Node *node = (Directory_Listener_Node*) Win32_Directory_Listener_Node *node = (Win32_Directory_Listener_Node*)
allocate_node(&vars->free_sentinel); allocate_node(&vars->free_sentinel);
if (node){ if (node){
if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){ if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){
@ -475,9 +169,9 @@ add_listener(File_Track_System *system, char *filename){
node->listener.dir = dir; node->listener.dir = dir;
node->listener.user_count = 1; node->listener.user_count = 1;
dir_lookup.entry->hash = dir_hash; win32_entry->hash = dir_hash;
dir_lookup.entry->dir = dir; win32_entry->dir = dir;
dir_lookup.entry->listener_node = node; win32_entry->listener_node = node;
++tables->tracked_count; ++tables->tracked_count;
} }
else{ else{
@ -501,7 +195,7 @@ add_listener(File_Track_System *system, char *filename){
} }
} }
else{ else{
Directory_Listener_Node *node = dir_lookup.entry->listener_node; Win32_Directory_Listener_Node *node = win32_entry->listener_node;
++node->listener.user_count; ++node->listener.user_count;
} }
} }
@ -525,7 +219,7 @@ add_listener(File_Track_System *system, char *filename){
File_Track_Result 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;
File_Track_Vars *vars = to_vars_(system); Win32_File_Track_Vars *vars = to_vars(system);
EnterCriticalSection(&vars->table_lock); EnterCriticalSection(&vars->table_lock);
@ -551,16 +245,17 @@ remove_listener(File_Track_System *system, char *filename){
if (getinfo_result){ if (getinfo_result){
File_Index dir_hash = internal_get_file_index(dir_info); File_Index dir_hash = internal_get_file_index(dir_info);
File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash); File_Track_Entry *dir_lookup = tracking_system_lookup_entry(tables, dir_hash);
Win32_File_Track_Entry *win32_dir = (Win32_File_Track_Entry*)dir_lookup;
Assert(!entry_is_available(dir_lookup.entry)); Assert(!entry_is_available(dir_lookup));
Directory_Listener_Node *node = dir_lookup.entry->listener_node; Win32_Directory_Listener_Node *node = win32_dir->listener_node;
--node->listener.user_count; --node->listener.user_count;
if (node->listener.user_count == 0){ if (node->listener.user_count == 0){
insert_node(&vars->free_sentinel, &node->node); insert_node(&vars->free_sentinel, &node->node);
CloseHandle(dir_lookup.entry->dir); CloseHandle(win32_dir->dir);
internal_free_slot(tables, dir_lookup.entry); internal_free_slot(tables, dir_lookup);
} }
} }
else{ else{
@ -582,62 +277,16 @@ remove_listener(File_Track_System *system, char *filename){
File_Track_Result 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;
File_Track_Vars *vars = to_vars_(system); Win32_File_Track_Vars *vars = to_vars(system);
EnterCriticalSection(&vars->table_lock); EnterCriticalSection(&vars->table_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);
if (original_tables->size < size){ if (result == FileTrack_Good){
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->file_table = sizeof(*tables);
tables->max = max_number_of_entries;
}
if (tables->max > original_tables->max){
uint32_t original_max = original_tables->max;
// 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;
}
}
tables->tracked_count = original_tables->tracked_count;
}
// NOTE(allen): Update to the new table
vars->tables = mem; vars->tables = mem;
} }
else{
result = FileTrack_MemoryTooSmall;
}
} }
else{
result = FileTrack_MemoryTooSmall;
}
LeaveCriticalSection(&vars->table_lock); LeaveCriticalSection(&vars->table_lock);
return(result); return(result);
@ -646,13 +295,13 @@ move_track_system(File_Track_System *system, void *mem, int32_t size){
File_Track_Result 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;
File_Track_Vars *vars = to_vars_(system); Win32_File_Track_Vars *vars = to_vars(system);
EnterCriticalSection(&vars->table_lock); EnterCriticalSection(&vars->table_lock);
if (sizeof(Directory_Listener_Node) <= size){ if (sizeof(Win32_Directory_Listener_Node) <= size){
Directory_Listener_Node *listener = (Directory_Listener_Node*)mem; Win32_Directory_Listener_Node *listener = (Win32_Directory_Listener_Node*)mem;
int32_t count = size / sizeof(Directory_Listener_Node); int32_t count = size / sizeof(Win32_Directory_Listener_Node);
for (int32_t i = 0; i < count; ++i, ++listener){ for (int32_t i = 0; i < count; ++i, ++listener){
insert_node(&vars->free_sentinel, &listener->node); insert_node(&vars->free_sentinel, &listener->node);
} }
@ -669,7 +318,7 @@ expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size
File_Track_Result 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;
File_Track_Vars *vars = to_vars_(system); Win32_File_Track_Vars *vars = to_vars(system);
EnterCriticalSection(&vars->table_lock); EnterCriticalSection(&vars->table_lock);
@ -684,8 +333,11 @@ get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t *
&overlapped, &overlapped,
0)){ 0)){
Directory_Listener *listener_ptr = (Directory_Listener*)overlapped; Win32_Directory_Listener *listener_ptr = (Win32_Directory_Listener*)overlapped;
Directory_Listener listener = *listener_ptr;
// NOTE(allen): Get a copy of the state of this node so we can set the node
// to work listening for changes again right away.
Win32_Directory_Listener listener = *listener_ptr;
ZeroStruct(listener_ptr->overlapped); ZeroStruct(listener_ptr->overlapped);
ReadDirectoryChangesW(listener_ptr->dir, ReadDirectoryChangesW(listener_ptr->dir,
@ -705,14 +357,16 @@ get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t *
info = (FILE_NOTIFY_INFORMATION*)(listener_buffer + offset); info = (FILE_NOTIFY_INFORMATION*)(listener_buffer + offset);
int32_t len = info->FileNameLength / 2; int32_t len = info->FileNameLength / 2;
int32_t dir_len =GetFinalPathNameByHandle(listener.dir, 0, 0, FILE_NAME_NORMALIZED); int32_t dir_len = GetFinalPathNameByHandle(listener.dir, 0, 0,
FILE_NAME_NORMALIZED);
int32_t req_size = dir_len + 1 + len; int32_t req_size = dir_len + 1 + len;
*size = req_size; *size = req_size;
if (req_size < max){ if (req_size < max){
int32_t pos = 0; int32_t pos = 0;
pos = GetFinalPathNameByHandle(listener.dir, buffer, max, FILE_NAME_NORMALIZED); pos = GetFinalPathNameByHandle(listener.dir, buffer, max,
buffer[pos++] = '/'; FILE_NAME_NORMALIZED);
buffer[pos++] = '\\';
for (int32_t i = 0; i < len; ++i, ++pos){ for (int32_t i = 0; i < len; ++i, ++pos){
buffer[pos] = (char)info->FileName[i]; buffer[pos] = (char)info->FileName[i];
@ -757,32 +411,38 @@ get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t *
File_Track_Result 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;
File_Track_Vars *vars = to_vars_(system); Win32_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; DWORD win32_result = 0;
for (; index < max; ++index){ // NOTE(allen): Close all the handles stored in the table.
File_Track_Entry *entry = entries + index; {
if (!entry_is_available(entry)){ File_Track_Tables *tables = to_tables(vars);
if (!CloseHandle(entry->dir)){
win32_result = 1; 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)){
Win32_File_Track_Entry *win32_entry = (Win32_File_Track_Entry*)entry;
if (!CloseHandle(win32_entry->dir)){
win32_result = 1;
}
} }
} }
} }
if (!CloseHandle(vars->iocp)){ // NOTE(allen): Close all the global track system resources.
win32_result = 1; {
if (!CloseHandle(vars->iocp)){
win32_result = 1;
}
DeleteCriticalSection(&vars->table_lock);
} }
DeleteCriticalSection(&vars->table_lock);
if (win32_result){ if (win32_result){
result = FileTrack_FileSystemError; result = FileTrack_FileSystemError;
} }

View File

@ -55,7 +55,7 @@ CUSTOM_COMMAND_SIG(load_lots_of_files){
app->free_file_list(app, list); app->free_file_list(app, list);
// TODO(allen): Pass this time test! // TODO(allen): Pass this time test!
TEST_TIME_E(); //TEST_TIME_E();
} }
CUSTOM_COMMAND_SIG(reopen_test){ CUSTOM_COMMAND_SIG(reopen_test){