void schedule() { /* azioni da compiere quando non c'e' nessun thread pronto ad eseguire */ if (emptyThreadQ(&readyQueue) && currentThread == NULL) { prova(); if (threadCount == 1)/* se c'e' solo il SSI -> normal system shutdown */ HALT(); /* chiamo la HALT ROM routine */ else if (threadCount > 0 && softBlockCount == 0) {/* deadlock */ PANIC(); /* chiamo la PANIC ROM routine */ } else if (threadCount > 0 && softBlockCount > 0) { /* in attesa di un interrupt -> wait state */ /* se ci sono thread in attesa dello pseudo tick, * carico il valore dello pseudo clock nel registro della cpu.*/ if (!emptyThreadQ(&waitForPseudoClockQueue)) { SET_IT(SCHED_PSEUDO_CLOCK); } /* impostiamo lo stato del processore con gli interrupt abilitati*/ setSTATUS(getSTATUS() | STATUS_IEc | STATUS_INT_UNMASKED); for (;;); } } else { /* Se non c'è nessun Thread in esecuzione ma c'e n'è almeno uno nella readyQueue allora carico un thread*/ if (currentThread == NULL) { currentThread = removeThread(&readyQueue); currentThread->elapsedTime = 0; currentThread->startTime = GET_TODLOW; SET_IT(SCHED_TIME_SLICE); /* Altrimenti se è passato il SCHED_TIME_SLICE rimuovo il thread corrente dall'esecuzione*/ } else if (currentThread->elapsedTime >= SCHED_TIME_SLICE) { //in questo modo do priorità all'SSI if (currentThread != tcb_SSI) { insertThread(&readyQueue, currentThread); /*Carico un nuovo thread*/ currentThread = removeThread(&readyQueue); } currentThread->elapsedTime = 0; currentThread->startTime = GET_TODLOW; /* Se e' scattato lo pseudo clock non settiamo il timer a 5 ms * dato che scattera' subito l'interrupt dello pseudo clock */ if (!isPseudoClock) SET_IT(SCHED_TIME_SLICE); } /* carico lo stato del thread nel processore dalla sua tcb */ LDST(&(currentThread->t_state)); } }
/* * This function is called when the calling user-level thread ends its * execution. It should schedule the first user-level thread in the ready * queue for running. */ void uthread_exit() { sem_wait(&mutex); if(initialized == 0) { fprintf(stderr, "Error, system needs to be initialized \n"); sem_post(&mutex); exit(EXIT_FAILURE); } num_running_threads--; struct thread* temp = removeThread(); sem_post(&mutex); if(temp == NULL) { exit(EXIT_SUCCESS); } sem_wait(&mutex); if(CALLING_THREAD->type == 1) { num_io_threads--; num_running_threads++; } CALLING_THREAD = temp; sem_post(&mutex); setcontext(temp->context); // clone(doSetContext, (void *)malloc(16384), CLONE_VM, temp->context); free(temp); }
/// Remove killed threads int pthread_kill(pthread_t thread, int signum) { if(signum == SIGKILL) { removeThread(thread); } return InterposeRoot::pthread_kill(thread, signum); }
void* threadStarter(void *threadv) { Thread *thread = threadv; Object *callable = stackGetThisObject(thread); stackPop(thread); executeCallableExtern(callable, NULL, thread); removeThread(thread); return NULL; }
ErrorCode ProcessBase::suspend() { std::set<Thread *> threads; enumerateThreads([&](Thread *thread) { threads.insert(thread); }); for (auto thread : threads) { switch (thread->state()) { case Thread::kInvalid: DS2BUG("trying to suspend tid %" PRI_PID " in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kStepped: case Thread::kStopped: case Thread::kTerminated: DS2LOG(Debug, "not suspending tid %" PRI_PID ", already in state %s", thread->tid(), Stringify::ThreadState(thread->state())); if (thread->state() == Thread::kTerminated) { removeThread(thread->tid()); } break; case Thread::kRunning: { DS2LOG(Debug, "suspending tid %" PRI_PID, thread->tid()); ErrorCode error = thread->suspend(); switch (error) { case kSuccess: break; case kErrorProcessNotFound: DS2LOG(Debug, "tried to suspended tid %" PRI_PID " which is already dead", thread->tid()); removeThread(thread->tid()); return error; default: DS2LOG(Warning, "failed suspending tid %" PRI_PID ", error=%s", thread->tid(), Stringify::Error(error)); return error; } } break; } } return kSuccess; }
ErrorCode Process::suspend() { std::set<Thread *> threads; enumerateThreads([&](Thread *thread) { threads.insert(thread); }); for (auto thread : threads) { Architecture::CPUState state; if (thread->state() != Thread::kRunning) { thread->readCPUState(state); } DS2LOG(Debug, "tid %d state %d at pc %#" PRIx64, thread->tid(), thread->state(), thread->state() == Thread::kStopped ? (uint64_t)state.pc() : 0); if (thread->state() == Thread::kRunning) { ErrorCode error; DS2LOG(Debug, "suspending tid %d", thread->tid()); error = thread->suspend(); if (error == kSuccess) { DS2LOG(Debug, "suspended tid %d at pc %#" PRIx64, thread->tid(), (uint64_t)state.pc()); thread->readCPUState(state); } else if (error == kErrorProcessNotFound) { // // Thread is dead. // removeThread(thread->tid()); DS2LOG(Debug, "tried to suspended tid %d which is already dead", thread->tid()); } else { return error; } } else if (thread->state() == Thread::kTerminated) { // // Thread is dead. // removeThread(thread->tid()); } } return kSuccess; }
// This is a utility function for detach. void ProcessBase::cleanup() { std::set<Thread *> threads; for (auto thread : _threads) { threads.insert(thread.second); } for (auto thread : threads) { removeThread(thread->tid()); } _threads.clear(); _currentThread = nullptr; }
void ScriptInterface::addThread(ScriptInterface *pChild) { if (caller == pChild) return; if (caller) caller->addThread(pChild); else { if (pChild == this) return; removeThread(pChild); childs.push_back(pChild); } }
void vHavokCpuJobThreadPool::setNumThreads(int numThreads) { if ( numThreads > MAX_NUM_THREADS ) { numThreads = MAX_NUM_THREADS; } while( m_sharedThreadData.m_numThreads < numThreads ) { addThread(); } while( m_sharedThreadData.m_numThreads > numThreads ) { removeThread(); } }
void *create_bomb(void *arg) { Block *bomb; bomb = (Block *) arg; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); pthread_mutex_lock(&mutex); addThread(pthread_self()); pthread_mutex_unlock(&mutex); while(bomb->row <= bomb->screen_height) { if(bomb->row == bomb->screen_height - 1) { pthread_mutex_lock(&mutex); if(bomb->col >= bomb->block->start && bomb->col <= bomb->block->end) { increaseScore(); displayScore(); } else { flash(); decreaseScore(score); displayScore(); } delete_bomb(bomb); pthread_mutex_unlock(&mutex); break; } pthread_mutex_lock(&mutex); SET_COLOR_BLUE; move(bomb->row, bomb->col); delch(); insch(' '); (bomb->row)++; SET_COLOR_RED; pthread_mutex_unlock(&mutex); refresh(); pthread_testcancel(); napms(150); } pthread_mutex_lock(&mutex); bomb->cols[bomb->col] = 0; removeThread(pthread_self()); pthread_mutex_unlock(&mutex); free(bomb); pthread_exit((void *) 0); }
/* * The calling thread requests to yield the kernel thread to another process. It * should save the context of current running thread and load the first one on * the ready queue (assuming the scheduling algorithm used is FCFS). The * function returns 0 on success and -1 otherwise. */ int uthread_yield() { sem_wait(&mutex); if(initialized == 0) { fprintf(stderr, "Error, system needs to be initialized \n"); sem_post(&mutex); return -1; } struct thread* nextWaiting = removeThread(); struct thread* currRun = CALLING_THREAD; sem_post(&mutex); if(nextWaiting != NULL) { swapcontext(currRun->context, nextWaiting->context); sem_wait(&mutex); CALLING_THREAD = nextWaiting; sem_post(&mutex); free(nextWaiting); } return 0; }
/* * The calling thread calls this function before it requests for I/O operations * (scanf, printf, read, write, etc.) We assume that when this function is * called, the state of the calling thread transits from running state to * waiting state and will not run on CPU actively. Therefore, it will create * a new kernel thread and schedule the first thread in the ready queue to run * (assuming the scheduling algorithm used is FCFS). This calling user-level * thread will remain associated with its current kernel thread, initiating I/O * and then waiting for it to complete. This function returns 0 on success and * -1 otherwise */ int uthread_startIO() { sem_wait(&mutex); if(initialized == 0) { fprintf(stderr, "Error, system needs to be initialized \n"); sem_post(&mutex); return -1; } struct thread* temp = removeThread(); temp->type = 1; sem_post(&mutex); if(temp != NULL) { sem_wait(&mutex); num_io_threads++; sem_post(&mutex); clone(doSetContext, malloc(16384), CLONE_VM, temp->context); free(temp); } return 0; }
void Thread_exit(int code) { BOOL result; T current = Thread_self(); removeThread(current); ENTERCRITICAL; if (current->joinlist != NULL) { T t, n; int count = 0; assert(current->join); for (t = current->joinlist; t != NULL; t = n) { t->code = code; n = t->link; t->link = NULL; count++; } current->joinlist = NULL; result = ReleaseSemaphore(current->join, count, NULL); assert(result == TRUE); } result = CloseHandle(current->join); assert(result == TRUE); current->join = NULL; if (join0count > 0 && nthreads == 1) { assert(join0count == 1); result = ReleaseSemaphore(join0, 1, NULL); assert(result == TRUE); } if (nthreads == 0) { result = CloseHandle(join0); assert(result == TRUE); } FREE(current); LEAVECRITICAL; _endthreadex(code); }
void ProcessBase::remove(ThreadBase *thread) { removeThread(thread->tid()); }
ErrorCode Process::wait(int *rstatus) { int status, signal; ProcessInfo info; ErrorCode err; pid_t tid; // We have at least one thread when we start waiting on a process. DS2ASSERT(!_threads.empty()); continue_waiting: err = super::wait(&status); if (err != kSuccess) return err; DS2LOG(Debug, "stopped: status=%d", status); if (WIFEXITED(status)) { err = super::wait(&status); DS2LOG(Debug, "exited: status=%d", status); _currentThread->updateStopInfo(status); _terminated = true; if (rstatus != nullptr) { *rstatus = status; } return kSuccess; } DS2BUG("not implemented"); switch (_currentThread->_stopInfo.event) { case StopInfo::kEventNone: switch (_currentThread->_stopInfo.reason) { case StopInfo::kReasonNone: ptrace().resume(ProcessThreadId(_pid, tid), info); goto continue_waiting; default: DS2ASSERT(false); goto continue_waiting; } case StopInfo::kEventExit: case StopInfo::kEventKill: DS2LOG(Debug, "thread %d is exiting", tid); // // Killing the main thread? // // Note(sas): This might be buggy; the main thread exiting // doesn't mean that the process is dying. // if (tid == _pid && _threads.size() == 1) { DS2LOG(Debug, "last thread is exiting"); break; } // // Remove and release the thread associated with this pid. // removeThread(tid); goto continue_waiting; case StopInfo::kEventStop: if (getInfo(info) != kSuccess) { DS2LOG(Error, "couldn't get process info for pid %d", _pid); goto continue_waiting; } signal = _currentThread->_stopInfo.signal; if (signal == SIGSTOP || signal == SIGCHLD) { // // Silently ignore SIGSTOP, SIGCHLD and SIGRTMIN (this // last one used for thread cancellation) and continue. // // Note(oba): The SIGRTMIN defines expands to a glibc // call, this due to the fact the POSIX standard does // not mandate that SIGRT* defines to be user-land // constants. // // Note(sas): This is probably partially dead code as // ptrace().step() doesn't work on ARM. // // Note(sas): Single-step detection should be higher up, not // only for SIGSTOP, SIGCHLD and SIGRTMIN, but for every // signal that we choose to ignore. // bool stepping = (_currentThread->state() == Thread::kStepped); if (signal == SIGSTOP) { signal = 0; } else { DS2LOG(Debug, "%s due to special signal, tid=%d status=%#x signal=%s", stepping ? "stepping" : "resuming", tid, status, strsignal(signal)); } ErrorCode error; if (stepping) { error = ptrace().step(ProcessThreadId(_pid, tid), info, signal); } else { error = ptrace().resume(ProcessThreadId(_pid, tid), info, signal); } if (error != kSuccess) { DS2LOG(Warning, "cannot resume thread %d error=%d", tid, error); } goto continue_waiting; } else if (_passthruSignals.find(signal) != _passthruSignals.end()) { ptrace().resume(ProcessThreadId(_pid, tid), info, signal); goto continue_waiting; } else { // // This is a signal that we want to transmit back to the // debugger. // break; } } if (!(WIFEXITED(status) || WIFSIGNALED(status))) { // // Suspend the process, this must be done after updating // the thread trap info. // suspend(); } if ((WIFEXITED(status) || WIFSIGNALED(status))) { _terminated = true; } if (rstatus != nullptr) { *rstatus = status; } return kSuccess; }
ErrorCode Process::wait() { int status, signal; bool stepping; ProcessInfo info; ThreadId tid; // We have at least one thread when we start waiting on a process. DS2ASSERT(!_threads.empty()); while (!_threads.empty()) { tid = blocking_waitpid(-1, &status, __WALL); if (tid <= 0) { return kErrorProcessNotFound; } DS2LOG(Debug, "tid %" PRI_PID " %s", tid, Stringify::WaitStatus(status)); auto threadIt = _threads.find(tid); if (threadIt == _threads.end()) { // If we don't know about this thread yet, but it has a WIFEXITED() or a // WIFSIGNALED() status (i.e.: it terminated), it means we already // cleaned up the thread object (e.g.: in Process::suspend), but we // hadn't waitpid()'d it yet. Avoid re-creating a Thread object here. if (WIFEXITED(status) || WIFSIGNALED(status)) { goto continue_waiting; } // A new thread has appeared that we didn't know about. Create the // Thread object and return. DS2LOG(Debug, "creating new thread tid=%d", tid); _currentThread = new Thread(this, tid); return kSuccess; } else { _currentThread = threadIt->second; } stepping = _currentThread->_state == Thread::kStepped; _currentThread->updateStopInfo(status); switch (_currentThread->_stopInfo.event) { case StopInfo::kEventNone: // If the thread is stopped for no reason, it means the debugger (ds2) // sent a SIGSTOP to it while it was already stopped for another reason, // the SIGSTOP was queued, and now we're getting this notification. We // have to ignore this and just let the thread continue. // If `stepping` is true, it means the thread was actually being // single-stepped before stopping, so instead of doing a `resume()`, we // have to do a new `step()`. if (stepping) { _currentThread->step(); } else { _currentThread->resume(); } goto continue_waiting; case StopInfo::kEventExit: case StopInfo::kEventKill: DS2LOG(Debug, "thread %d is exiting", tid); // // Killing the main thread? // // Note(sas): This might be buggy; the main thread exiting // doesn't mean that the process is dying. // if (tid == _pid && _threads.size() == 1) { DS2LOG(Debug, "last thread is exiting"); break; } // // Remove and release the thread associated with this pid. // removeThread(tid); goto continue_waiting; case StopInfo::kEventStop: signal = _currentThread->_stopInfo.signal; DS2LOG(Debug, "stopped tid=%" PRI_PID " status=%#x signal=%s", tid, status, Stringify::Signal(signal)); if (_passthruSignals.find(signal) != _passthruSignals.end()) { DS2LOG(Debug, "%s passed through to thread %" PRI_PID ", not stopping", Stringify::Signal(signal), tid); _currentThread->resume(signal); goto continue_waiting; } else { // // This is a signal that we want to transmit back to the // debugger. // break; } } break; continue_waiting: _currentThread = nullptr; continue; } if (!(WIFEXITED(status) || WIFSIGNALED(status)) || tid != _pid) { // // Suspend the process, this must be done after updating // the thread trap info. // suspend(); } if ((WIFEXITED(status) || WIFSIGNALED(status)) && tid == _pid) { _terminated = true; } return kSuccess; }
ErrorCode Process::wait() { int status, signal; ProcessInfo info; ThreadId tid; // We have at least one thread when we start waiting on a process. DS2ASSERT(!_threads.empty()); while (!_threads.empty()) { tid = blocking_waitpid(-1, &status, __WALL); DS2LOG(Debug, "wait tid=%d status=%#x", tid, status); if (tid <= 0) return kErrorProcessNotFound; auto threadIt = _threads.find(tid); if (threadIt == _threads.end()) { // If we don't know about this thread yet, but it has a WIFEXITED() or a // WIFSIGNALED() status (i.e.: it terminated), it means we already // cleaned up the thread object (e.g.: in Process::suspend), but we // hadn't waitpid()'d it yet. Avoid re-creating a Thread object here. if (WIFEXITED(status) || WIFSIGNALED(status)) { goto continue_waiting; } // A new thread has appeared that we didn't know about. Create the // Thread object and return. DS2LOG(Debug, "creating new thread tid=%d", tid); _currentThread = new Thread(this, tid); return kSuccess; } else { _currentThread = threadIt->second; } _currentThread->updateStopInfo(status); switch (_currentThread->_stopInfo.event) { case StopInfo::kEventNone: _currentThread->resume(); goto continue_waiting; case StopInfo::kEventExit: case StopInfo::kEventKill: DS2LOG(Debug, "thread %d is exiting", tid); // // Killing the main thread? // // Note(sas): This might be buggy; the main thread exiting // doesn't mean that the process is dying. // if (tid == _pid && _threads.size() == 1) { DS2LOG(Debug, "last thread is exiting"); break; } // // Remove and release the thread associated with this pid. // removeThread(tid); goto continue_waiting; case StopInfo::kEventStop: signal = _currentThread->_stopInfo.signal; DS2LOG(Debug, "stopped tid=%d status=%#x signal=%s", tid, status, Stringify::Signal(signal)); if (_passthruSignals.find(signal) != _passthruSignals.end()) { _currentThread->resume(signal); goto continue_waiting; } else { // // This is a signal that we want to transmit back to the // debugger. // break; } } break; continue_waiting: _currentThread = nullptr; continue; } if (!(WIFEXITED(status) || WIFSIGNALED(status)) || tid != _pid) { // // Suspend the process, this must be done after updating // the thread trap info. // suspend(); } if ((WIFEXITED(status) || WIFSIGNALED(status)) && tid == _pid) { _terminated = true; } return kSuccess; }
ErrorCode Process::wait(int *status, bool hang) { if (_terminated) return kSuccess; DEBUG_EVENT de; bool keepGoing = true; while (keepGoing) { BOOL result = WaitForDebugEvent(&de, hang ? INFINITE : 0); if (!result) return Platform::TranslateError(); keepGoing = false; switch (de.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: #define CHECK_AND_CLOSE(HAN) \ do { \ if ((de.u.CreateProcessInfo.HAN) != NULL) \ CloseHandle(de.u.CreateProcessInfo.HAN); \ } while (0) CHECK_AND_CLOSE(hFile); CHECK_AND_CLOSE(hProcess); CHECK_AND_CLOSE(hThread); #undef CHECK_AND_CLOSE return kSuccess; case EXIT_PROCESS_DEBUG_EVENT: _terminated = true; return kSuccess; case CREATE_THREAD_DEBUG_EVENT: { auto threadHandle = de.u.CreateThread.hThread; auto tid = GetThreadId(threadHandle); // No need to save the new thread pointer, as it gets added automatically // to the process. new Thread(this, tid, threadHandle); resume(); keepGoing = true; } break; case EXIT_THREAD_DEBUG_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); auto tid = threadIt->second->tid(); ContinueDebugEvent(_pid, tid, DBG_CONTINUE); removeThread(threadIt->second->tid()); keepGoing = true; } break; case RIP_EVENT: DS2LOG(Fatal, "debug event RIP"); case EXCEPTION_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); threadIt->second->updateState(de); } break; default: DS2BUG("unknown debug event code: %lu", de.dwDebugEventCode); } } return kSuccess; }
/// Intercept calls to pthread_exit void pthread_exit(void* retval) { removeThread(InterposeRoot::pthread_self()); InterposeRoot::pthread_exit(retval); }
/// Remove cancelled threads int pthread_cancel(pthread_t thread) { removeThread(thread); return InterposeRoot::pthread_cancel(thread); }