blob: 94f4e7b59fd11a957ce464969ea9bd21cdaad332 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "object/suspend_token_dispatcher.h"
#include <lib/counters.h>
#include <zircon/errors.h>
#include <zircon/rights.h>
#include <zircon/types.h>
#include <fbl/alloc_checker.h>
#include <kernel/auto_lock.h>
#include <object/process_dispatcher.h>
#include <object/thread_dispatcher.h>
KCOUNTER(dispatcher_suspend_token_create_count, "dispatcher.suspend_token.create")
KCOUNTER(dispatcher_suspend_token_destroy_count, "dispatcher.suspend_token.destroy")
namespace {
// Suspends a process or thread.
// TODO(fxbug.dev/30807): Add support for jobs.
zx_status_t SuspendTask(fbl::RefPtr<Dispatcher> task) {
if (auto thread = DownCastDispatcher<ThreadDispatcher>(&task)) {
if (thread.get() == ThreadDispatcher::GetCurrent())
return ZX_ERR_NOT_SUPPORTED;
return thread->Suspend();
}
if (auto process = DownCastDispatcher<ProcessDispatcher>(&task)) {
if (process.get() == ProcessDispatcher::GetCurrent())
return ZX_ERR_NOT_SUPPORTED;
return process->Suspend();
}
return ZX_ERR_WRONG_TYPE;
}
// Resumes a process or thread.
// TODO(fxbug.dev/30807): Add support for jobs.
void ResumeTask(fbl::RefPtr<Dispatcher> task) {
if (auto thread = DownCastDispatcher<ThreadDispatcher>(&task)) {
thread->Resume();
return;
}
if (auto process = DownCastDispatcher<ProcessDispatcher>(&task)) {
process->Resume();
return;
}
__UNREACHABLE;
}
} // namespace
zx_status_t SuspendTokenDispatcher::Create(fbl::RefPtr<Dispatcher> task,
KernelHandle<SuspendTokenDispatcher>* handle,
zx_rights_t* rights) {
fbl::AllocChecker ac;
KernelHandle new_handle(fbl::AdoptRef(new (&ac) SuspendTokenDispatcher()));
if (!ac.check())
return ZX_ERR_NO_MEMORY;
zx_status_t status = SuspendTask(task);
if (status != ZX_OK)
return status;
// Save the task after suspending so that on_zero_handles() resumes it.
new_handle.dispatcher()->task_ = ktl::move(task);
*rights = default_rights();
*handle = ktl::move(new_handle);
return ZX_OK;
}
SuspendTokenDispatcher::SuspendTokenDispatcher() {
kcounter_add(dispatcher_suspend_token_create_count, 1);
}
SuspendTokenDispatcher::~SuspendTokenDispatcher() {
kcounter_add(dispatcher_suspend_token_destroy_count, 1);
}
void SuspendTokenDispatcher::on_zero_handles() {
// This is only called once and we're done with |task_| afterwards so we can move it out.
if (task_)
ResumeTask(ktl::move(task_));
}