blob: 6ff46f9ba530cb7880fc8138fc8d25fbeb4ca9a6 [file] [log] [blame]
//===--- Concurrency-libdispatch.cpp --------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "SourceKit/Support/Concurrency.h"
#include "SourceKit/Config/config.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Threading.h"
#include <dispatch/dispatch.h>
#include <Block.h>
using namespace SourceKit;
void *Semaphore::Impl::create(long count) {
return dispatch_semaphore_create(count);
}
void Semaphore::Impl::signal(Ty Obj) {
dispatch_semaphore_signal(dispatch_semaphore_t(Obj));
}
bool Semaphore::Impl::wait(Ty Obj) {
return dispatch_semaphore_wait(dispatch_semaphore_t(Obj),
DISPATCH_TIME_FOREVER);
}
bool Semaphore::Impl::wait(Ty Obj, long milliseconds) {
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW,
milliseconds * NSEC_PER_MSEC);
return dispatch_semaphore_wait(dispatch_semaphore_t(Obj), timeout);
}
void Semaphore::Impl::retain(Ty Obj) {
dispatch_retain(dispatch_semaphore_t(Obj));
}
void Semaphore::Impl::release(Ty Obj) {
dispatch_release(dispatch_semaphore_t(Obj));
}
static dispatch_queue_priority_t toDispatchPriority(WorkQueue::Priority Prio) {
switch (Prio) {
case WorkQueue::Priority::High: return DISPATCH_QUEUE_PRIORITY_HIGH;
case WorkQueue::Priority::Default: return DISPATCH_QUEUE_PRIORITY_DEFAULT;
case WorkQueue::Priority::Low: return DISPATCH_QUEUE_PRIORITY_LOW;
case WorkQueue::Priority::Background:
return DISPATCH_QUEUE_PRIORITY_BACKGROUND;
}
llvm_unreachable("Invalid priority");
}
static dispatch_queue_attr_t toDispatchDequeuing(WorkQueue::Dequeuing DeqKind) {
switch (DeqKind) {
case WorkQueue::Dequeuing::Concurrent: return DISPATCH_QUEUE_CONCURRENT;
case WorkQueue::Dequeuing::Serial: return DISPATCH_QUEUE_SERIAL;
}
llvm_unreachable("Invalid dequeuing kind");
}
static dispatch_queue_t getDispatchGlobalQueue(WorkQueue::Priority Prio) {
return dispatch_get_global_queue(toDispatchPriority(Prio), 0);
}
void *WorkQueue::Impl::create(Dequeuing DeqKind, Priority Prio,
llvm::StringRef Label) {
const char *LabelCStr = 0;
llvm::SmallString<128> LabelStr(Label);
if (!Label.empty()) {
LabelStr.push_back('\0');
LabelCStr = LabelStr.begin();
}
dispatch_queue_t queue =
dispatch_queue_create(LabelCStr, toDispatchDequeuing(DeqKind));
setPriority(queue, Prio);
return queue;
}
namespace {
struct ExecuteOnLargeStackInfo {
dispatch_block_t BlockToRun;
~ExecuteOnLargeStackInfo() {
Block_release(BlockToRun);
}
};
} // end anonymous namespace
static void executeBlock(void *Data) {
auto ExecuteInfo = (ExecuteOnLargeStackInfo*)Data;
ExecuteInfo->BlockToRun();
delete ExecuteInfo;
}
static void executeOnLargeStackThread(void *Data) {
static const size_t ThreadStackSize = 8 << 20; // 8 MB.
llvm::llvm_execute_on_thread(executeBlock, Data, ThreadStackSize);
}
static std::pair<void *, WorkQueue::DispatchFn>
toCFunction(void *Ctx, WorkQueue::DispatchFn Fn, bool isStackDeep) {
if (!isStackDeep)
return std::make_pair(Ctx, Fn);
auto ExecuteInfo = new ExecuteOnLargeStackInfo;
#if HAVE_DISPATCH_BLOCK_CREATE
// Transfer attributes of the calling thread, such as QOS class,
// os_activity_t, etc.
if (&dispatch_block_create) {
ExecuteInfo->BlockToRun = dispatch_block_create(DISPATCH_BLOCK_ASSIGN_CURRENT,
^{ Fn(Ctx); });
} else {
ExecuteInfo->BlockToRun = Block_copy(^{ Fn(Ctx); });
}
#else
ExecuteInfo->BlockToRun = Block_copy(^{ Fn(Ctx); });
#endif
return std::make_pair(ExecuteInfo, executeOnLargeStackThread);
}
void WorkQueue::Impl::dispatch(Ty Obj, const DispatchData &Fn) {
void *Context;
WorkQueue::DispatchFn CFn;
std::tie(Context, CFn) = toCFunction(Fn.getContext(), Fn.getFunction(),
Fn.isStackDeep());
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_async_f(queue, Context, CFn);
}
void WorkQueue::Impl::dispatchSync(Ty Obj, const DispatchData &Fn) {
void *Context;
WorkQueue::DispatchFn CFn;
std::tie(Context, CFn) = toCFunction(Fn.getContext(), Fn.getFunction(),
Fn.isStackDeep());
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_sync_f(queue, Context, CFn);
}
void WorkQueue::Impl::dispatchBarrier(Ty Obj, const DispatchData &Fn) {
void *Context;
WorkQueue::DispatchFn CFn;
std::tie(Context, CFn) = toCFunction(Fn.getContext(), Fn.getFunction(),
Fn.isStackDeep());
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_barrier_async_f(queue, Context, CFn);
}
void WorkQueue::Impl::dispatchBarrierSync(Ty Obj, const DispatchData &Fn) {
void *Context;
WorkQueue::DispatchFn CFn;
std::tie(Context, CFn) = toCFunction(Fn.getContext(), Fn.getFunction(),
Fn.isStackDeep());
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_barrier_sync_f(queue, Context, CFn);
}
void WorkQueue::Impl::dispatchOnMain(const DispatchData &Fn) {
void *Context;
WorkQueue::DispatchFn CFn;
std::tie(Context, CFn) = toCFunction(Fn.getContext(), Fn.getFunction(),
Fn.isStackDeep());
dispatch_async_f(dispatch_get_main_queue(), Context, CFn);
}
void WorkQueue::Impl::dispatchConcurrent(Priority Prio, const DispatchData &Fn) {
void *Context;
WorkQueue::DispatchFn CFn;
std::tie(Context, CFn) = toCFunction(Fn.getContext(), Fn.getFunction(),
Fn.isStackDeep());
dispatch_async_f(getDispatchGlobalQueue(Prio), Context, CFn);
}
void WorkQueue::Impl::suspend(Ty Obj) {
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_suspend(queue);
}
void WorkQueue::Impl::resume(Ty Obj) {
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_resume(queue);
}
void WorkQueue::Impl::setPriority(Ty Obj, Priority Prio) {
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_set_target_queue(queue, getDispatchGlobalQueue(Prio));
}
llvm::StringRef WorkQueue::Impl::getLabel(const Ty Obj) {
dispatch_queue_t queue = dispatch_queue_t(Obj);
return dispatch_queue_get_label(queue);
}
void WorkQueue::Impl::retain(Ty Obj) {
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_retain(queue);
}
void WorkQueue::Impl::release(Ty Obj) {
dispatch_queue_t queue = dispatch_queue_t(Obj);
dispatch_release(queue);
}