first version of file track system

This commit is contained in:
Allen Webster 2016-08-22 15:31:19 -04:00
parent 74407bc0f6
commit 2c0b575f5c
11 changed files with 1767 additions and 208 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 <stdint.h>
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

File diff suppressed because it is too large Load Diff

10
filetrack/build.bat Normal file
View File

@ -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

436
filetrack/filetrack_main.c Normal file
View File

@ -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 <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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

114
filetrack/filetrack_test.c Normal file
View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
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

21
thing.txt Normal file
View File

@ -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},
};

View File

@ -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