/** * Attach an activity to an interpreter instance * * @param instance The interpreter instance involved. * * @return Either an existing activity, or a new activity created for * this thread. */ RexxActivity *ActivityManager::attachThread() { // it's possible we already have an activity active for this thread. That // most likely occurs in nested RexxStart() calls. RexxActivity *oldActivity = findActivity(); // we have an activity created for this thread already. The interpreter instance // should already have handled the case of an attach for an already attached thread. // so we're going to have a new activity to create, and potentially an existing one to // suspend // we need to lock the kernel to have access to the memory manager to // create this activity. lockKernel(); RexxActivity *activityObject = createCurrentActivity(); // Do we have a nested interpreter call occurring on the same thread? We need to // mark the old activity as suspended, and chain this to the new activity. if (oldActivity != OREF_NULL) { oldActivity->setSuspended(true); // this pushes this down the stack. activityObject->setNestedActivity(oldActivity); } unlockKernel(); /* release kernel semaphore */ // now we need to have this activity become the kernel owner. activityObject->requestAccess(); // this will help ensure that the code after the request access call // is only executed after access acquired. sentinel = true; // belt-and-braces. Make sure the current activity is explicitly set to // this activity before leaving. currentActivity = activityObject; return activityObject; }
/** * Get a root activity for a new interpreter instance. * * @return The newly created activity. */ Activity *ActivityManager::getRootActivity() { // it's possible we already have an activity active for this thread. That // most likely occurs in nested RexxStart() calls. Get that activity first, // and if we have one, we'll need to push this down. Activity *oldActivity = findActivity(); // we need to lock the kernel to have access to the memory manager to // create this activity. lockKernel(); // get a new activity object Activity *activityObject = createCurrentActivity(); unlockKernel(); // mark this as the root activity for an interpreter instance. Some operations // are only permitted from the root threads. activityObject->setInterpreterRoot(); // Do we have a nested interpreter call occurring on the same thread? We need to // mark the old activity as suspended, and chain this to the new activity. if (oldActivity != OREF_NULL) { oldActivity->setSuspended(true); // this pushes this down the stack. activityObject->setNestedActivity(oldActivity); } // now we need to have this activity become the kernel owner. activityObject->requestAccess(); // this will help ensure that the code after the request access call // is only executed after access acquired. sentinel = true; activityObject->activate(); // let the activity know it's in use, potentially nested // belt-and-braces. Make sure the current activity is explicitly set to // this activity before leaving. currentActivity = activityObject; return activityObject; }
/** * Add a waiting activity to the waiting queue. * * @param waitingAct The activity to queue up. * @param release If true, the kernel lock should be released on completion. */ void ActivityManager::addWaitingActivity(RexxActivity *waitingAct, bool release ) { ResourceSection lock("ActivityManager::addWaitingActivity", 0); // need the control block locks addWaitingActivityCount += 1; // nobody waiting yet? If the release flag is true, we already have the // kernel lock, but nobody is waiting. In theory, this can't really // happen, but we can return immediately if that is true. if (waitingActivities.empty()) { // we're done if we already have the lock and the queue is empty. if (release) { return; } // add to the end waitingActivities.push_back(waitingAct); // this will ensure this happens before the lock is released sentinel = false; // we should end up getting the lock immediately, but you never know. lock.release(); // release the lock now } else { // add to the end waitingActivities.push_back(waitingAct); // this will ensure this happens before the lock is released sentinel = false; // we're going to wait until posted, so make sure the run semaphore is cleared waitingAct->clearWait(); sentinel = true; lock.release(); // release the lock now sentinel = false; // if we are the current kernel semaphore owner, time to release this // so other waiters can if (release) { unlockKernel(); } SysActivity::yield(); // yield the thread waitingAct->waitForDispatch(); // wait for this thread to get dispatched again } sentinel = true; lockKernel(); // get the kernel lock now // belt and braces. it is possible the dispatcher was // reentered on the same thread, in which case we have an // earlier stack frame waiting on the same semaphore. Clear it so it // get get reposted later. waitingAct->clearWait(); sentinel = false; lock.reacquire(); // get the resource lock back sentinel = false; // another memory barrier // We only get dispatched if we end up at the front of the queue again, // so just pop the front element. waitingActivities.pop_front(); sentinel = true; // if there's something else in the queue, then post the run semaphore of // the head element so that it wakes up next and starts waiting on the // run semaphore if (hasWaiters()) { waitingActivities.front()->postDispatch(); } // the setting of the sentinel variables acts as a memory barrier to // ensure that the assignment of currentActivitiy occurs precisely at this point. sentinel = false; currentActivity = waitingAct; /* set new current activity */ sentinel = true; /* and new active settings */ Numerics::setCurrentSettings(waitingAct->getNumericSettings()); }