static void S_fill(InStream *self, int64_t amount) { InStreamIVARS *const ivars = InStream_IVARS(self); FileWindow *const window = ivars->window; const int64_t virtual_file_pos = SI_tell(self); const int64_t real_file_pos = virtual_file_pos + ivars->offset; const int64_t remaining = ivars->len - virtual_file_pos; // Throw an error if the requested amount would take us beyond EOF. if (amount > remaining) { THROW(ERR, "Read past EOF of %o (pos: %u64 len: %u64 request: %u64)", ivars->filename, virtual_file_pos, ivars->len, amount); } // Make the request. if (FH_Window(ivars->file_handle, window, real_file_pos, amount)) { char *fw_buf = FileWindow_Get_Buf(window); int64_t fw_offset = FileWindow_Get_Offset(window); int64_t fw_len = FileWindow_Get_Len(window); char *const window_limit = fw_buf + fw_len; ivars->buf = fw_buf - fw_offset // theoretical start of real file + ivars->offset // top of virtual file + virtual_file_pos; // position within virtual file ivars->limit = window_limit - ivars->buf > remaining ? ivars->buf + remaining : window_limit; } else { Err *error = Err_get_error(); CB_catf(Err_Get_Mess(error), " (%o)", ivars->filename); RETHROW(INCREF(error)); } }
static void S_fill(InStream *self, int64_t amount) { FileWindow *const window = self->window; const int64_t virtual_file_pos = SI_tell(self); const int64_t real_file_pos = virtual_file_pos + self->offset; const int64_t remaining = self->len - virtual_file_pos; // Throw an error if the requested amount would take us beyond EOF. if (amount > remaining) { THROW(ERR, "Read past EOF of %o (pos: %u64 len: %u64 request: %u64)", self->filename, virtual_file_pos, self->len, amount); } // Make the request. if (FH_Window(self->file_handle, window, real_file_pos, amount)) { char *const window_limit = window->buf + window->len; self->buf = window->buf - window->offset // theoretical start of real file + self->offset // top of virtual file + virtual_file_pos; // position within virtual file self->limit = window_limit - self->buf > remaining ? self->buf + remaining : window_limit; } else { Err *error = Err_get_error(); CB_catf(Err_Get_Mess(error), " (%o)", self->filename); RETHROW(INCREF(error)); } }
bool TestBatchRunner_Run_Batch_IMP(TestBatchRunner *self, TestBatch *batch) { struct try_run_tests_context args; args.runner = self; args.batch = batch; Err *err = Err_trap(S_try_run_tests, &args); bool failed = false; if (err) { failed = true; String *mess = Err_Get_Mess(err); Err_warn_mess((String*)INCREF(mess)); } if (self->num_failed > 0) { failed = true; TestFormatter_batch_comment(self->formatter, "%d/%d tests failed.\n", self->num_failed, self->test_num); } if (self->test_num != self->num_planned) { failed = true; TestFormatter_batch_comment(self->formatter, "Bad plan: You planned %d tests but ran" " %d.\n", self->num_planned, self->test_num); } return !failed; }
static void S_verify_abstract_error(TestBatch *batch, Err_Attempt_t routine, void *context, const char *name) { char message[100]; sprintf(message, "%s() is abstract", name); Err *error = Err_trap(routine, context); TEST_TRUE(batch, error != NULL && Err_Is_A(error, ERR) && CB_Find_Str(Err_Get_Mess(error), "bstract", 7) != -1, message); DECREF(error); }
void THROW(VTable *vtable, char *pattern, ...) { va_list args; Err_Make_t make = METHOD_PTR(CERTIFY(vtable, VTABLE), Lucy_Err_Make); Err *err = (Err*)CERTIFY(make(NULL), ERR); CharBuf *mess = Err_Get_Mess(err); va_start(args, pattern); CB_VCatF(mess, pattern, args); va_end(args); Err_do_throw(err); }
void Err_throw_at(VTable *vtable, const char *file, int line, const char *func, const char *pattern, ...) { va_list args; Err_Make_t make = METHOD_PTR(CERTIFY(vtable, VTABLE), Lucy_Err_Make); Err *err = (Err*)CERTIFY(make(NULL), ERR); CharBuf *mess = Err_Get_Mess(err); va_start(args, pattern); S_vcat_mess(mess, file, line, func, pattern, args); va_end(args); Err_do_throw(err); }
static void test_threads(TestBatchRunner *runner) { Err_set_error(Err_new(Str_newf("main"))); HANDLE thread = CreateThread(NULL, 0, S_err_thread, runner, 0, NULL); TEST_TRUE(runner, thread != NULL, "CreateThread succeeds"); DWORD event = WaitForSingleObject(thread, INFINITE); TEST_INT_EQ(runner, event, WAIT_OBJECT_0, "WaitForSingleObject succeeds"); CloseHandle(thread); String *mess = Err_Get_Mess(Err_get_error()); TEST_TRUE(runner, Str_Equals_Utf8(mess, "main", 4), "thread doesn't clobber global error"); }
static void test_threads(TestBatchRunner *runner) { Err_set_error(Err_new(Str_newf("main"))); int err; pthread_t thread; err = pthread_create(&thread, NULL, S_err_thread, runner); TEST_INT_EQ(runner, err, 0, "pthread_create succeeds"); err = pthread_join(thread, NULL); TEST_INT_EQ(runner, err, 0, "pthread_join succeeds"); String *mess = Err_Get_Mess(Err_get_error()); TEST_TRUE(runner, Str_Equals_Utf8(mess, "main", 4), "thread doesn't clobber global error"); }
bool_t LFLock_request(LockFileLock *self) { Hash *file_data; bool_t wrote_json; bool_t success = false; bool_t deletion_failed = false; if (Folder_Exists(self->folder, self->lock_path)) { Err_set_error((Err*)LockErr_new(CB_newf("Can't obtain lock: '%o' exists", self->lock_path))); return false; } // Create the "locks" subdirectory if necessary. CharBuf *lock_dir_name = (CharBuf*)ZCB_WRAP_STR("locks", 5); if (!Folder_Exists(self->folder, lock_dir_name)) { if (!Folder_MkDir(self->folder, lock_dir_name)) { Err *mkdir_err = (Err*)CERTIFY(Err_get_error(), ERR); LockErr *err = LockErr_new(CB_newf("Can't create 'locks' directory: %o", Err_Get_Mess(mkdir_err))); // Maybe our attempt failed because another process succeeded. if (Folder_Find_Folder(self->folder, lock_dir_name)) { DECREF(err); } else { // Nope, everything failed, so bail out. Err_set_error((Err*)err); return false; } } } // Prepare to write pid, lock name, and host to the lock file as JSON. file_data = Hash_new(3); Hash_Store_Str(file_data, "pid", 3, (Obj*)CB_newf("%i32", (int32_t)PID_getpid())); Hash_Store_Str(file_data, "host", 4, INCREF(self->host)); Hash_Store_Str(file_data, "name", 4, INCREF(self->name)); // Write to a temporary file, then use the creation of a hard link to // ensure atomic but non-destructive creation of the lockfile with its // complete contents. wrote_json = Json_spew_json((Obj*)file_data, self->folder, self->link_path); if (wrote_json) { success = Folder_Hard_Link(self->folder, self->link_path, self->lock_path); if (!success) { Err *hard_link_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(CB_newf("Failed to obtain lock at '%o': %o", self->lock_path, Err_Get_Mess(hard_link_err)))); } deletion_failed = !Folder_Delete(self->folder, self->link_path); } else { Err *spew_json_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(CB_newf("Failed to obtain lock at '%o': %o", self->lock_path, Err_Get_Mess(spew_json_err)))); } DECREF(file_data); // Verify that our temporary file got zapped. if (wrote_json && deletion_failed) { CharBuf *mess = MAKE_MESS("Failed to delete '%o'", self->link_path); Err_throw_mess(ERR, mess); } return success; }
bool LFLock_Request_IMP(LockFileLock *self) { LockFileLockIVARS *const ivars = LFLock_IVARS(self); bool success = false; if (Folder_Exists(ivars->folder, ivars->lock_path)) { Err_set_error((Err*)LockErr_new(Str_newf("Can't obtain lock: '%o' exists", ivars->lock_path))); return false; } // Create the "locks" subdirectory if necessary. String *lock_dir_name = (String*)SSTR_WRAP_UTF8("locks", 5); if (!Folder_Exists(ivars->folder, lock_dir_name)) { if (!Folder_MkDir(ivars->folder, lock_dir_name)) { Err *mkdir_err = (Err*)CERTIFY(Err_get_error(), ERR); LockErr *err = LockErr_new(Str_newf("Can't create 'locks' directory: %o", Err_Get_Mess(mkdir_err))); // Maybe our attempt failed because another process succeeded. if (Folder_Find_Folder(ivars->folder, lock_dir_name)) { DECREF(err); } else { // Nope, everything failed, so bail out. Err_set_error((Err*)err); return false; } } } // Prepare to write pid, lock name, and host to the lock file as JSON. Hash *file_data = Hash_new(3); Hash_Store_Utf8(file_data, "pid", 3, (Obj*)Str_newf("%i32", (int32_t)PID_getpid())); Hash_Store_Utf8(file_data, "host", 4, INCREF(ivars->host)); Hash_Store_Utf8(file_data, "name", 4, INCREF(ivars->name)); String *json = Json_to_json((Obj*)file_data); DECREF(file_data); // Write to a temporary file, then use the creation of a hard link to // ensure atomic but non-destructive creation of the lockfile with its // complete contents. OutStream *outstream = Folder_Open_Out(ivars->folder, ivars->link_path); if (!outstream) { ERR_ADD_FRAME(Err_get_error()); DECREF(json); return false; } struct lockfile_context context; context.outstream = outstream; context.json = json; Err *json_error = Err_trap(S_write_lockfile_json, &context); bool wrote_json = !json_error; DECREF(outstream); DECREF(json); if (wrote_json) { success = Folder_Hard_Link(ivars->folder, ivars->link_path, ivars->lock_path); if (!success) { Err *hard_link_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(Str_newf("Failed to obtain lock at '%o': %o", ivars->lock_path, Err_Get_Mess(hard_link_err)))); } } else { Err_set_error((Err*)LockErr_new(Str_newf("Failed to obtain lock at '%o': %o", ivars->lock_path, Err_Get_Mess(json_error)))); DECREF(json_error); } // Verify that our temporary file got zapped. bool deletion_failed = !Folder_Delete(ivars->folder, ivars->link_path); if (deletion_failed) { String *mess = MAKE_MESS("Failed to delete '%o'", ivars->link_path); Err_throw_mess(ERR, mess); } return success; }