void Program::ThreadMonitor::theProcessHasStopped(bool killed, int exitstatus) { // prevent against invalid code if (YUNI_UNLIKELY(pEndTime == 0)) { assert(false and "endTime is invalid"); pEndTime = currentTime(); } // execution time for the sub-process sint64 duration = (pEndTime >= pStartTime) ? (pEndTime - pStartTime) : 0; // Making sure that the process ID is invalid { MutexLocker locker(procinfo.mutex); if (YUNI_UNLIKELY(not procinfo.running)) // already stopped - should never happen return; procinfo.running = false; procinfo.processInput = -1; procinfo.exitstatus = exitstatus; procinfo.duration = duration; } if (!(!stream)) { stream->onStop(killed, exitstatus, duration); // remove the reference to the stream as soon as possible stream = nullptr; } }
bool QueueThread::onExecute() { // Notify the scheduler that this thread has begun its work pQueueService.registerWorker(this); // Asking for the next job while (pQueueService.pWaitingRoom.pop(pJob)) { // Execute the job, via a wrapper for symbol visibility issues Yuni::Private::QueueService::JobAccessor<Yuni::Job::IJob>::Execute(*pJob, this); // We must release our pointer to the job here to avoid its destruction // in `pQueueService.nextJob()` (when `pJob` is re-assigned). // This method uses a lock and the destruction of the job may take some time. // Obviously, there is absolutely no guarantee that the job will be destroyed // at this point but we don't really care pJob = nullptr; // Cancellation point if (YUNI_UNLIKELY(shouldAbort())) // We have to stop as soon as possible, no need for hibernation return false; } // loop for retrieving jobs to execute // Returning true, for hibernation return true; }
DBI::Error Transaction::truncate(const AnyString& tablename) { if (YUNI_UNLIKELY(not IsValidIdentifier(tablename))) return errInvalidIdentifier; assert(!(!pChannel)); // the adapter ::yn_dbi_adapter& adapter = pChannel->adapter; // The DBI interface should provide the most appropriate way for // truncating a table (autoincrement / cascade...) if (YUNI_LIKELY(adapter.truncate)) { return (DBI::Error) adapter.truncate(adapter.dbh, tablename.c_str(), tablename.size()); } else { // Fallback to a failsafe method // -- stmt << "TRUNCATE " << tablename << ';'; // The SQL command Truncate is not supported by all databases. `DELETE FROM` // is not the most efficient way for truncating a table // but it should work almost everywhere String stmt; stmt << "DELETE FROM " << tablename << ';'; return perform(stmt); } }
Cursor Transaction::prepare(const AnyString& stmt) { assert(!(!pChannel)); // the adapter ::yn_dbi_adapter& adapter = pChannel->adapter; if (YUNI_UNLIKELY(nullHandle == pTxHandle)) { if (errNone != pChannel->begin(pTxHandle)) return Cursor(adapter, nullptr); } // query handle void* handle = nullptr; if (YUNI_LIKELY(not stmt.empty() and adapter.dbh)) { assert(adapter.query_new != NULL and "invalid adapter query_new"); assert(adapter.query_ref_acquire != NULL and "invalid adapter query_ref_acquire"); assert(adapter.query_ref_release != NULL and "invalid adapter query_ref_release"); adapter.query_new(&handle, adapter.dbh, stmt.c_str(), stmt.size()); } return Cursor(adapter, handle); }
Program::ProcessSharedInfo::~ProcessSharedInfo() { if (YUNI_UNLIKELY(timeoutThread.get())) { // should never go in this section assert(false and "the thread for handling the timeout has not been properly stopped"); timeoutThread->stop(); } if (thread and thread->release()) delete thread; }
int Program::wait(sint64* duration) { auto envptr = pEnv; if (YUNI_UNLIKELY(!envptr)) { if (duration) *duration = 0; return 0; } ProcessSharedInfo& env = *envptr; ThreadMonitor* thread = nullptr; // checking environment { MutexLocker locker(env.mutex); if (not env.running or (nullptr == env.thread)) { if (duration) *duration = env.duration; return env.exitstatus; } // capture the thread thread = env.thread; thread->addRef(); } // wait for the end of the thread assert(thread != nullptr); thread->wait(); MutexLocker locker(env.mutex); // since the thread has finished, we can safely destroy it if (env.thread) env.thread->release(); env.thread = nullptr; if (thread->release()) delete thread; if (duration) *duration = env.duration; return env.exitstatus; }
void Program::commandLine(AnyString cmd) { // remove all whitespaces cmd.trim(); auto envptr = pEnv; // keeping a reference to the current env if (!envptr) { envptr = std::make_shared<ProcessSharedInfo>(); pEnv = envptr; } ProcessSharedInfo& env = *envptr; MutexLocker locker(env.mutex); env.executable.clear(); env.arguments.clear(); if (cmd.empty()) return; String* str = &env.executable; char instring = '\0'; const AnyString::null_iterator end = cmd.utf8end(); for (AnyString::const_utf8iterator i = cmd.utf8begin(); i != end; ++i) { char c = *i; switch (c) { default: { *str += i.value(); break; } case '"': [[fallthrough]]; case '\'': { if (instring == '\0') { instring = c; } else { if (instring == c) instring = '\0'; else *str += c; } break; } case '\\': { ++i; if (YUNI_UNLIKELY(i == end)) return; c = *i; switch (c) { case 'n': (*str) += '\n'; break; case 't': (*str) += '\t'; break; case 'r': (*str) += '\r'; break; case 'b': (*str) += '\b'; break; case 'f': (*str) += '\f'; break; case 'v': (*str) += '\v'; break; case '0': (*str) += '\0'; break; case 'e': [[fallthrough]]; case 'a': [[fallthrough]]; case 'E': break; default: (*str) << '\\' << c; break; } break; } case ' ': [[fallthrough]]; case '\t': { if (instring == '\0') { if (not str->empty()) { env.arguments.push_back(nullptr); str = &(env.arguments.back()); } } else *str += c; break; } } } }