Object c_AwaitAllWaitHandle::createAAWH(T start, T stop, F1 iterNext, F2 getCell) { auto ctx_idx = std::numeric_limits<context_idx_t>::max(); uint32_t cnt = 0; for (auto iter = start; iter != stop; iter = iterNext(iter, stop)) { prepareChild(getCell(iter), ctx_idx, cnt); } if (!cnt) { return Object{returnEmpty()}; } auto result = Alloc(cnt); auto next = &result->m_children[cnt]; uint32_t idx = cnt - 1; for (auto iter = start; iter != stop; iter = iterNext(iter, stop)) { addChild(getCell(iter), next, idx); } assert(next == &result->m_children[0]); result->initialize(ctx_idx); return Object{std::move(result)}; }
Object c_AwaitAllWaitHandle::FromMixedArray(const MixedArray* dependencies) { auto const start = dependencies->data(); auto const stop = start + dependencies->iterLimit(); auto ctx_idx = std::numeric_limits<context_idx_t>::max(); int32_t cnt = 0; for (auto iter = start; iter < stop; ++iter) { if (MixedArray::isTombstone(iter->data.m_type)) continue; prepareChild(tvToCell(&iter->data), ctx_idx, cnt); } if (!cnt) return returnEmpty(); auto result = Alloc(cnt); auto next = &result->m_children[cnt]; for (auto iter = start; iter < stop; ++iter) { if (MixedArray::isTombstone(iter->data.m_type)) continue; addChild(tvToCell(&iter->data), next); } assert(next == &result->m_children[0]); result->initialize(ctx_idx); return Object{std::move(result)}; }
void c_AsyncGeneratorWaitHandle::await(c_WaitableWaitHandle* child) { // Prepare child for establishing dependency. May throw. prepareChild(child); // Set up the dependency. m_child = child; setState(STATE_BLOCKED); blockOn(m_child); }
void c_AsyncGeneratorWaitHandle::await(c_WaitableWaitHandle* child) { // Prepare child for establishing dependency. May throw. prepareChild(child); // Set up the dependency. setState(STATE_BLOCKED); m_child = child; m_child->getParentChain() .addParent(m_blockable, AsioBlockable::Kind::AsyncGeneratorWaitHandle); }
void c_AsyncFunctionWaitHandle::await(Offset resumeOffset, c_WaitableWaitHandle* child) { // Prepare child for establishing dependency. May throw. prepareChild(child); // Suspend the async function. resumable()->setResumeAddr(nullptr, resumeOffset); // Set up the dependency. setState(STATE_BLOCKED); m_children[0].setChild(child); }
Object c_AwaitAllWaitHandle::FromVector(const BaseVector* dependencies) { auto const start = dependencies->data(); auto const stop = start + dependencies->size(); auto ctx_idx = std::numeric_limits<context_idx_t>::max(); int32_t cnt = 0; for (auto iter = start; iter < stop; ++iter) { prepareChild(tvAssertCell(iter), ctx_idx, cnt); } if (!cnt) return returnEmpty(); auto result = Alloc(cnt); auto next = &result->m_children[cnt]; for (auto iter = start; iter < stop; ++iter) { addChild(tvAssertCell(iter), next); } assert(next == &result->m_children[0]); result->initialize(ctx_idx); return Object{std::move(result)}; }
Object c_AwaitAllWaitHandle::FromMap(const BaseMap* dependencies) { auto const start = dependencies->firstElm(); auto const stop = dependencies->elmLimit(); auto ctx_idx = std::numeric_limits<context_idx_t>::max(); int32_t cnt = 0; for (auto iter = start; iter != stop; iter = BaseMap::nextElm(iter, stop)) { prepareChild(tvAssertCell(&iter->data), ctx_idx, cnt); } if (!cnt) return returnEmpty(); auto result = Alloc(cnt); auto next = &result->m_children[cnt]; for (auto iter = start; iter != stop; iter = BaseMap::nextElm(iter, stop)) { addChild(tvAssertCell(&iter->data), next); } assert(next == &result->m_children[0]); result->initialize(ctx_idx); return Object{std::move(result)}; }
Object c_AwaitAllWaitHandle::FromPackedArray(const ArrayData* dependencies) { auto const start = reinterpret_cast<const TypedValue*>(dependencies + 1); auto const stop = start + dependencies->getSize(); auto ctx_idx = std::numeric_limits<context_idx_t>::max(); int32_t cnt = 0; for (auto iter = start; iter < stop; ++iter) { prepareChild(tvToCell(iter), ctx_idx, cnt); } if (!cnt) return returnEmpty(); auto result = Alloc(cnt); auto next = &result->m_children[cnt]; for (auto iter = start; iter < stop; ++iter) { addChild(tvToCell(iter), next); } assert(next == &result->m_children[0]); result->initialize(ctx_idx); return Object{std::move(result)}; }
Object c_AwaitAllWaitHandle::Create(Iter iter) { auto ctx_idx = std::numeric_limits<context_idx_t>::max(); uint32_t cnt = 0; auto toCell = convert ? [](TypedValue tv) { return tvToCell(tv); } : [](TypedValue tv) { return tvAssertCell(tv); }; iter([&](TypedValue v) { prepareChild(toCell(v), ctx_idx, cnt); }); if (!cnt) { return Object{returnEmpty()}; } auto result = Alloc(cnt); auto next = &result->m_children[cnt]; uint32_t idx = cnt - 1; iter([&](TypedValue v) { addChild(toCell(v), next, idx); }); assert(next == &result->m_children[0]); result->initialize(ctx_idx); return Object{std::move(result)}; }
void Subprocess::spawnInternal( std::unique_ptr<const char*[]> argv, const char* executable, Options& options, const std::vector<std::string>* env, int errFd) { // Parent work, pre-fork: create pipes std::vector<int> childFds; // Close all of the childFds as we leave this scope SCOPE_EXIT { // These are only pipes, closing them shouldn't fail for (int cfd : childFds) { CHECK_ERR(::close(cfd)); } }; int r; for (auto& p : options.fdActions_) { if (p.second == PIPE_IN || p.second == PIPE_OUT) { int fds[2]; r = ::pipe(fds); checkUnixError(r, "pipe"); PipeInfo pinfo; pinfo.direction = p.second; int cfd; if (p.second == PIPE_IN) { // Child gets reading end pinfo.parentFd = fds[1]; cfd = fds[0]; } else { pinfo.parentFd = fds[0]; cfd = fds[1]; } p.second = cfd; // ensure it gets dup2()ed pinfo.childFd = p.first; childFds.push_back(cfd); pipes_.push_back(pinfo); } } // This should already be sorted, as options.fdActions_ is DCHECK(std::is_sorted(pipes_.begin(), pipes_.end())); // Note that the const casts below are legit, per // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html char** argVec = const_cast<char**>(argv.get()); // Set up environment std::unique_ptr<const char*[]> envHolder; char** envVec; if (env) { envHolder = cloneStrings(*env); envVec = const_cast<char**>(envHolder.get()); } else { envVec = environ; } // Block all signals around vfork; see http://ewontfix.com/7/. // // As the child may run in the same address space as the parent until // the actual execve() system call, any (custom) signal handlers that // the parent has might alter parent's memory if invoked in the child, // with undefined results. So we block all signals in the parent before // vfork(), which will cause them to be blocked in the child as well (we // rely on the fact that Linux, just like all sane implementations, only // clones the calling thread). Then, in the child, we reset all signals // to their default dispositions (while still blocked), and unblock them // (so the exec()ed process inherits the parent's signal mask) // // The parent also unblocks all signals as soon as vfork() returns. sigset_t allBlocked; r = sigfillset(&allBlocked); checkUnixError(r, "sigfillset"); sigset_t oldSignals; r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals); checkPosixError(r, "pthread_sigmask"); SCOPE_EXIT { // Restore signal mask r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr); CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail }; pid_t pid = vfork(); if (pid == 0) { int errnoValue = prepareChild(options, &oldSignals); if (errnoValue != 0) { childError(errFd, kChildFailure, errnoValue); } errnoValue = runChild(executable, argVec, envVec, options); // If we get here, exec() failed. childError(errFd, kExecFailure, errnoValue); } // In parent. Make sure vfork() succeeded. checkUnixError(pid, errno, "vfork"); // Child is alive. We have to be very careful about throwing after this // point. We are inside the constructor, so if we throw the Subprocess // object will have never existed, and the destructor will never be called. // // We should only throw if we got an error via the errFd, and we know the // child has exited and can be immediately waited for. In all other cases, // we have no way of cleaning up the child. pid_ = pid; returnCode_ = ProcessReturnCode(RV_RUNNING); }