mt_throws Result TcpServer::open () { fd = WSASocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); if (fd == INVALID_SOCKET) { exc_throw (WSAException, WSAGetLastError ()); exc_throw (InternalException, InternalException::BackendError); return Result::Failure; } { u_long value = 1; int const res = ioctlsocket (fd, FIONBIO, &value); if (res != 0) { if (res != SOCKET_ERROR) { exc_throw (InternalException, InternalException::BackendMalfunction); logE_ (_func, "ioctlsocket(): unexpected return value: ", res); return Result::Failure; } int const error = WSAGetLastError (); exc_throw (WSAException, error); exc_push (InternalException, InternalException::BackendError); logE_ (_func, "ioctlsocket() failed: ", wsaErrorToString (error)); return Result::Failure; } } if (!_libMary_win32_setsockopt_reuseaddr (fd)) return Result::Failure; return Result::Success; }
static void xxfwrite(FILE * stream, const eu8 * ptr, size_t size) { size_t n = fwrite(ptr, size, 1, stream); if (n == 0) { if (ferror(stream)) exc_throw(MiscError, "fwrite failure:"); exc_throw(MiscError, "fwrite failure:"); } }
static eu8 * xxfread(FILE * stream, eu8 * ptr, size_t size) { size_t n = fread(ptr, size, 1, stream); if (n == 0) { if (ferror(stream)) exc_throw(MiscError, "fread failure:"); exc_throw(EOFIndicator, NULL); } return ptr; }
mt_throws AsyncIoResult NativeAsyncFile::write (ConstMemory const mem, Size * const ret_nwritten) { if (ret_nwritten) *ret_nwritten = 0; // According to POSIX, if we pass a value larger than SSIZE_MAX to read, // then the result is implementation-defined. Size len; if (mem.len() > SSIZE_MAX) len = SSIZE_MAX; else len = mem.len(); ssize_t const res = ::write (fd, mem.mem(), len); if (res == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { requestOutput (); return AsyncIoResult::Again; } if (errno == EINTR) return AsyncIoResult::Normal; if (errno == EPIPE) { // If there'll be a need to distinguish Eof from Error, then this // is the place to intervene. return AsyncIoResult::Error; } exc_throw (PosixException, errno); exc_push_ (IoException); return AsyncIoResult::Error; } else if (res < 0) { exc_throw (InternalException, InternalException::BackendMalfunction); return AsyncIoResult::Error; } if ((Size) res > len) { exc_throw (InternalException, InternalException::BackendMalfunction); return AsyncIoResult::Error; } if (ret_nwritten) *ret_nwritten = res; return AsyncIoResult::Normal; }
mt_throws Result Thread::spawn (bool const joinable) { this->ref (); mutex.lock (); GError *error = NULL; GThread * const tmp_thread = g_thread_create (wrapperThreadFunc, this, joinable ? TRUE : FALSE, &error); this->thread = tmp_thread; mutex.unlock (); if (tmp_thread == NULL) { exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "g_thread_create() failed: ", error->message, error->message ? strlen (error->message) : 0); g_clear_error (&error); this->unref (); return Result::Failure; } return Result::Success; }
static FILE * xfopen(const char * filename, const char * mode) { FILE * fh = fopen(filename, mode); if (fh == NULL) exc_throw(MiscError, "fopen(\"%s\", \"%s\") failure:", filename, mode); return fh; }
mt_throws AsyncIoResult NativeAsyncFile::read (Memory const mem, Size * const ret_nread) { if (ret_nread) *ret_nread = 0; // According to POSIX, if we pass a value larger than SSIZE_MAX to read, // then the result is implementation-defined. Size len; if (mem.len() > SSIZE_MAX) len = SSIZE_MAX; else len = mem.len(); ssize_t const res = ::read (fd, mem.mem(), len); if (res == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { requestInput (); return AsyncIoResult::Again; } if (errno == EINTR) return AsyncIoResult::Normal; exc_throw (PosixException, errno); exc_push_ (IoException); return AsyncIoResult::Error; } else if (res < 0) { exc_throw (InternalException, InternalException::BackendMalfunction); return AsyncIoResult::Error; } else if (res == 0) { return AsyncIoResult::Eof; } if ((Size) res > len) { exc_throw (InternalException, InternalException::BackendMalfunction); return AsyncIoResult::Error; } if (ret_nread) *ret_nread = res; return AsyncIoResult::Normal; }
mt_throws Result PollPollGroup::poll (Uint64 const timeout_microsec) { Time const start_microsec = getTimeMicroseconds (); Result ret_res = Result::Success; SelectedList selected_list; for (;;) { selected_list.clear (); Count cur_num_pollables = 1; struct pollfd pollfds [num_pollables]; pollfds [0].fd = trigger_pipe [0]; pollfds [0].events = POLLIN; { mutex.lock (); PollableList::iter iter (pollable_list); while (!pollable_list.iter_done (iter)) { PollableEntry * const pollable_entry = pollable_list.iter_next (iter); selected_list.append (pollable_entry); pollfds [cur_num_pollables].fd = pollable_entry->fd; pollfds [cur_num_pollables].events = #ifdef __linux__ POLLRDHUP; #else 0; #endif if (pollable_entry->need_input) pollfds [cur_num_pollables].events |= POLLIN; if (pollable_entry->need_output) pollfds [cur_num_pollables].events |= POLLOUT; pollable_entry->ref (); ++cur_num_pollables; } assert (cur_num_pollables == num_pollables + 1); mutex.unlock (); } Time elapsed_microsec; int nfds; { Time cur_microsec = getTimeMicroseconds (); if (cur_microsec < start_microsec) cur_microsec = start_microsec; elapsed_microsec = cur_microsec - start_microsec; int timeout; if (!got_deferred_tasks) { if (timeout_microsec != (Uint64) -1) { if (timeout_microsec > elapsed_microsec) { Uint64 const tmp_timeout = (timeout_microsec - elapsed_microsec) / 1000; timeout = (int) tmp_timeout; if ((Uint64) timeout != tmp_timeout || timeout < 0) timeout = Int_Max - 1; if (timeout == 0) timeout = 1; } else { timeout = 0; } } else { timeout = -1; } } else { timeout = 0; } mutex.lock (); if (triggered || timeout == 0) { block_trigger_pipe = true; timeout = 0; } else { block_trigger_pipe = false; } mutex.unlock (); nfds = ::poll (pollfds, cur_num_pollables, timeout); if (nfds == -1) { if (errno == EINTR) { SelectedList::iter iter (selected_list); mutex.lock (); while (!selected_list.iter_done (iter)) { PollableEntry * const pollable_entry = selected_list.iter_next (iter); pollable_entry->unref (); } mutex.unlock (); // Re-initializing pollfds. continue; } exc_throw (PosixException, errno); exc_push (InternalException, InternalException::BackendError); logE_ (_func, "poll() failed: ", errnoString (errno)); ret_res = Result::Failure; goto _poll_interrupted; } else if (nfds < 0) { logE_ (_func, "unexpected return value from poll(): ", nfds); exc_throw (InternalException, InternalException::BackendMalfunction); ret_res = Result::Failure; goto _poll_interrupted; } } mutex.lock (); block_trigger_pipe = true; bool const was_triggered = triggered; triggered = false; mutex.unlock (); got_deferred_tasks = false; if (frontend) frontend.call (frontend->pollIterationBegin); bool trigger_pipe_ready = false; { if (pollfds [0].revents & (POLLIN | POLLERR | POLLHUP #ifdef __linux__ | POLLRDHUP #endif )) trigger_pipe_ready = true; mutex.lock (); SelectedList::iter iter (selected_list); Count i = 1; while (!selected_list.iter_done (iter)) { PollableEntry * const pollable_entry = selected_list.iter_next (iter); if (nfds > 0 && pollable_entry->valid) { Uint32 event_flags = 0; if (pollfds [i].revents & POLLNVAL) { logW_ (_func, "POLLNVAL for pollable_entry " "0x", fmt_hex, (UintPtr) pollable_entry, ", " "fd ", pollable_entry->fd, " (", pollfds [i].fd, ")"); ++i; continue; } if (pollfds [i].revents & POLLIN) { pollable_entry->need_input = false; event_flags |= PollGroup::Input; } if (pollfds [i].revents & POLLOUT) { pollable_entry->need_output = false; event_flags |= PollGroup::Output; } if (pollfds [i].revents & POLLHUP #ifdef __linux__ || pollfds [i].revents & POLLRDHUP #endif ) { event_flags |= PollGroup::Hup; } if (pollfds [i].revents & POLLERR) { event_flags |= PollGroup::Error; } if (event_flags) { mutex.unlock (); pollable_entry->pollable.call (pollable_entry->pollable->processEvents, /*(*/ event_flags /*)*/); mutex.lock (); } } pollable_entry->unref (); ++i; } assert (i == cur_num_pollables); mutex.unlock (); } if (frontend) { bool extra_iteration_needed = false; frontend.call_ret (&extra_iteration_needed, frontend->pollIterationEnd); if (extra_iteration_needed) got_deferred_tasks = true; } if (trigger_pipe_ready) { if (!commonTriggerPipeRead (trigger_pipe [0])) { logF_ (_func, "commonTriggerPipeRead() failed: ", exc->toString()); return Result::Failure; } break; } if (was_triggered) break; if (elapsed_microsec >= timeout_microsec) { // Timeout expired. break; } } return ret_res; _poll_interrupted: SelectedList::iter iter (selected_list); mutex.lock (); while (!selected_list.iter_done (iter)) { PollableEntry * const pollable_entry = selected_list.iter_next (iter); pollable_entry->unref (); } mutex.unlock (); return ret_res; }
mt_throws Result loadModule (ConstMemory const filename, void * const app_specific) { #ifdef LIBMARY_GLIB GModule * const module = g_module_open ((gchar const*) makeString (filename)->cstr(), (GModuleFlags) (G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL)); if (!module) { exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "failed to open module ", filename, ": ", g_module_error()); return Result::Failure; } ModuleInitFunc init_ptr; if (!g_module_symbol (module, "libMary_moduleInit_wrapper", (gpointer*) &init_ptr)) { exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "failed to open module ", filename, ": " "g_module_symbol (\"libMary_moduleInit_wrapper\") failed: ", g_module_error()); return Result::Failure; } if (!init_ptr (app_specific)) { logE_ (_func, "could notd initialize module ", filename); return Result::Failure; } return Result::Success; #else libraryLock (); void * const handle = dlopen ( #ifdef LIBMARY_PLATFORM_MACOSX makeString (filename, ".0.dylib")->cstr(), #else makeString (filename, ".so")->cstr(), #endif RTLD_LAZY); if (!handle) { char const * const err_str = dlerror (); libraryUnlock (); exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "dlopen (", filename, ") failed: ", err_str); return Result::Failure; } dlerror (); // Clearing any old error conditions. See man dlsym(3). void * const init_ptr = dlsym (handle, "libMary_moduleInit"); if (!init_ptr) { char const * const err_str = dlerror (); libraryUnlock (); exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "dlsym (", filename, ", libMary_moduleInit) failed: ", err_str); return Result::Failure; } libraryUnlock (); bool const res = ((ModuleInitFunc) init_ptr) (app_specific); if (!res) { exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "module init failed: ", filename); } return res ? Result::Success : Result::Failure; #endif }
mt_throws Result updateTime () { LibMary_ThreadLocal * const tlocal = libMary_getThreadLocal(); #ifdef LIBMARY_PLATFORM_WIN32 DWORD const win_time_dw = timeGetTime(); if (tlocal->prv_win_time_dw >= win_time_dw) { tlocal->win_time_offs += 0x100000000; } tlocal->prv_win_time_dw = win_time_dw; DWORD const win_time = win_time_dw + tlocal->win_time_offs; Time const new_seconds = (Time) win_time / 1000; Time const new_microseconds = (Time) win_time * 1000; #else #ifdef __MACH__ mach_timespec_t mts; { #warning TODO call host_get_clock_service only once clock_serv_t clk; host_get_clock_service (mach_host_self(), SYSTEM_CLOCK, &clk); clock_get_time (clk, &mts); mach_port_deallocate (mach_task_self(), clk); } Time const new_seconds = mts.tv_sec; Time const new_microseconds = (Uint64) mts.tv_sec * 1000000 + (Uint64) mts.tv_nsec / 1000; #if 0 // Deprecated gint64 const mono_time = g_get_monotonic_time (); Int64 const mono_time = 0; Time const new_seconds = mono_time / 1000000; Time const new_microseconds = mono_time; #endif #else struct timespec ts; // Note that clock_gettime is well-optimized on Linux x86_64 and does not carry // full syscall overhead (depends on system configuration). int const res = clock_gettime (CLOCK_MONOTONIC, &ts); if (res == -1) { exc_throw (PosixException, errno); exc_push (InternalException, InternalException::BackendError); logE_ (_func, "clock_gettime() failed: ", errnoString (errno)); return Result::Failure; } else if (res != 0) { exc_throw (InternalException, InternalException::BackendError); logE_ (_func, "clock_gettime(): unexpected return value: ", res); return Result::Failure; } logD (time, _func, "tv_sec: ", ts.tv_sec, ", tv_nsec: ", ts.tv_nsec); Time const new_seconds = ts.tv_sec; Time const new_microseconds = (Uint64) ts.tv_sec * 1000000 + (Uint64) ts.tv_nsec / 1000; #endif #endif tlocal->time_log_frac = new_microseconds % 1000000 / 100; if (new_seconds >= tlocal->time_seconds) tlocal->time_seconds = new_seconds; else logW_ (_func, "seconds backwards: ", new_seconds, " (was ", tlocal->time_seconds, ")"); if (new_microseconds >= tlocal->time_microseconds) tlocal->time_microseconds = new_microseconds; else logW_ (_func, "microseconds backwards: ", new_microseconds, " (was ", tlocal->time_microseconds, ")"); logD (time, _func, fmt_hex, tlocal->time_seconds, ", ", tlocal->time_microseconds); if (tlocal->saved_monotime < tlocal->time_seconds || tlocal->saved_unixtime == 0) { // Updading saved unixtime once in a minute. if (tlocal->time_seconds - tlocal->saved_monotime >= 60 || tlocal->saved_unixtime == 0) { // Obtaining current unixtime. This is an extra syscall. tlocal->saved_unixtime = time (NULL); tlocal->saved_monotime = tlocal->time_seconds; } // Updating localtime (broken-down time). time_t const cur_unixtime = tlocal->saved_unixtime + (tlocal->time_seconds - tlocal->saved_monotime); tlocal->unixtime = cur_unixtime; // Note that we call tzset() in libMary_posixInit() for localtime_r() to work correctly. #ifdef LIBMARY_PLATFORM_WIN32 #ifdef LIBMARY_WIN32_SECURE_CRT if (localtime_s (&tlocal->localtime, &cur_unixtime) != 0) logF_ (_func, "localtime_s() failed"); #else libraryLock (); struct tm * const tmp_localtime = localtime (&cur_unixtime); if (tmp_localtime) { tlocal->localtime = *tmp_localtime; } libraryUnlock (); if (!tmp_localtime) logF_ (_func, "localtime() failed"); #endif #else // TODO FIXME This lib function is dog-slow and has huge synchronization overhead. localtime_r (&cur_unixtime, &tlocal->localtime); #endif } // long const timezone_abs = timezone >= 0 ? timezone : -timezone; long const timezone_abs = 0; // tlocal->timezone_str [0] = timezone >= 0 ? '+' : '-'; tlocal->timezone_str [0] = '+'; tlocal->timezone_str [1] = '0' + timezone_abs / 36000; tlocal->timezone_str [2] = '0' + timezone_abs / 3600 % 10; tlocal->timezone_str [3] = '0' + timezone_abs / 600 % 6; tlocal->timezone_str [4] = '0' + timezone_abs / 60 % 10; return Result::Success; }
static void xmkdir(const char * path, int mode) { if (mkdir(path, mode) < 0) exc_throw(MiscError, "mkdir(%s, 0%o) failure:", path, mode); }
static void xfseek0(FILE * stream, long offset) { if (fseek(stream, offset, SEEK_SET) < 0) exc_throw(MiscError, "fseek(stream, %ld, SEEK_SET) failure:",offset); }
mt_throws Result NativeAsyncFile::open (ConstMemory const filename, Uint32 const open_flags, FileAccessMode const access_mode) { int flags = 0; switch ((FileAccessMode::Value) access_mode) { case FileAccessMode::ReadOnly: flags = O_RDONLY; break; case FileAccessMode::WriteOnly: flags = O_WRONLY; break; case FileAccessMode::ReadWrite: flags = O_RDWR; break; default: unreachable (); } if (open_flags && FileOpenFlags::Create) flags |= O_CREAT; // TODO Specify behavior for Truncate & O_RDONLY combination. if (open_flags & FileOpenFlags::Truncate) flags |= O_TRUNC; // TODO Seek to the end of file instead. O_APPEND semantics is too complicated. if (open_flags & FileOpenFlags::Append) flags |= O_APPEND; StRef<String> const filename_str = st_grab (new (std::nothrow) String (filename)); for (;;) { /* NOTE: man 2 open does not mention EINTR as a possible return * value, while man 3 open _does_. This means that EINTR should * be handled for all invocations of open() in MyCpp (and all * over MyNC). */ fd = ::open (filename_str->cstr(), // Note that O_DIRECT affects kernel-level caching/buffering // and should not be set here. flags | O_NONBLOCK, S_IRUSR | S_IWUSR); if (fd == -1) { if (errno == EINTR) continue; if (errno == EAGAIN) { logD_ (_func, "EAGAIN"); break; } if (errno == EWOULDBLOCK) { logD_ (_func, "EWOULDBLOCK"); break; } exc_throw (PosixException, errno); exc_push_ (IoException); return Result::Failure; } break; } return Result::Success; }