+1
-4
@@ -1011,10 +1011,7 @@ class StubGenerator(
|
||||
return true
|
||||
}
|
||||
|
||||
return this.isDefined ||
|
||||
this.isVararg ||
|
||||
this.returnsRecord() ||
|
||||
this.parameters.map { it.type }.any { it.unwrapTypedefs() is RecordType }
|
||||
return true
|
||||
}
|
||||
|
||||
private fun FunctionType.requiresAdapterOnNative(): Boolean {
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ internal interface ContextUtils : RuntimeAware {
|
||||
val ClassDescriptor.typeInfoPtr: ConstPointer
|
||||
get() {
|
||||
return if (isExternal(this)) {
|
||||
constPointer(externalGlobal(this.typeInfoSymbolName, runtime.typeInfoType))
|
||||
constPointer(importGlobal(this.typeInfoSymbolName, runtime.typeInfoType))
|
||||
} else {
|
||||
context.llvmDeclarations.forClass(this).typeInfo
|
||||
}
|
||||
|
||||
+2
-1
@@ -1287,7 +1287,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
assert (!descriptor.isUnit())
|
||||
return if (codegen.isExternal(descriptor)) {
|
||||
val llvmType = codegen.getLLVMType(descriptor.defaultType)
|
||||
codegen.externalGlobal(descriptor.objectInstanceFieldSymbolName, llvmType)
|
||||
codegen.importGlobal(descriptor.objectInstanceFieldSymbolName, llvmType,
|
||||
threadLocal = true)
|
||||
} else {
|
||||
context.llvmDeclarations.forSingleton(descriptor).instanceFieldRef
|
||||
}
|
||||
|
||||
+4
-6
@@ -296,11 +296,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
} else {
|
||||
"kobjref:" + qualifyInternalName(descriptor)
|
||||
}
|
||||
val instanceFieldRef = LLVMAddGlobal(
|
||||
context.llvmModule,
|
||||
getLLVMType(descriptor.defaultType),
|
||||
symbolName
|
||||
)!!
|
||||
val instanceFieldRef = addGlobal(
|
||||
symbolName, getLLVMType(descriptor.defaultType), threadLocal = true)
|
||||
|
||||
return SingletonLlvmDeclarations(instanceFieldRef)
|
||||
}
|
||||
@@ -326,7 +323,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
// Fields are module-private, so we use internal name:
|
||||
val name = "kvar:" + qualifyInternalName(descriptor)
|
||||
|
||||
val storage = LLVMAddGlobal(context.llvmModule, getLLVMType(descriptor.type), name)!!
|
||||
val storage = addGlobal(
|
||||
name, getLLVMType(descriptor.type), threadLocal = true)
|
||||
|
||||
this.staticFields[descriptor] = StaticFieldLlvmDeclarations(storage)
|
||||
}
|
||||
|
||||
+17
-2
@@ -182,14 +182,29 @@ internal fun getGlobalType(ptrToGlobal: LLVMValueRef): LLVMTypeRef {
|
||||
return LLVMGetElementType(ptrToGlobal.type)!!
|
||||
}
|
||||
|
||||
internal fun ContextUtils.externalGlobal(name: String, type: LLVMTypeRef): LLVMValueRef {
|
||||
internal fun ContextUtils.addGlobal(name: String, type: LLVMTypeRef,
|
||||
threadLocal: Boolean = false): LLVMValueRef {
|
||||
assert(LLVMGetNamedGlobal(context.llvmModule, name) == null)
|
||||
val result = LLVMAddGlobal(context.llvmModule, type, name)!!
|
||||
if (threadLocal)
|
||||
LLVMSetThreadLocalMode(result, LLVMThreadLocalMode.LLVMLocalExecTLSModel)
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef,
|
||||
threadLocal: Boolean = false): LLVMValueRef {
|
||||
val found = LLVMGetNamedGlobal(context.llvmModule, name)
|
||||
if (found != null) {
|
||||
assert (getGlobalType(found) == type)
|
||||
assert (LLVMGetInitializer(found) == null)
|
||||
if (threadLocal)
|
||||
assert(LLVMGetThreadLocalMode(found) == LLVMThreadLocalMode.LLVMLocalExecTLSModel)
|
||||
return found
|
||||
} else {
|
||||
return LLVMAddGlobal(context.llvmModule, type, name)!!
|
||||
val result = LLVMAddGlobal(context.llvmModule, type, name)!!
|
||||
if (threadLocal)
|
||||
LLVMSetThreadLocalMode(result, LLVMThreadLocalMode.LLVMLocalExecTLSModel)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
if (annot != null) {
|
||||
val nameValue = annot.allValueArguments.values.single() as StringValue
|
||||
// TODO: use LLVMAddAlias?
|
||||
val global = LLVMAddGlobal(context.llvmModule, pointerType(runtime.typeInfoType), nameValue.value)
|
||||
val global = addGlobal(nameValue.value, pointerType(runtime.typeInfoType))
|
||||
LLVMSetInitializer(global, typeInfoGlobal)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -42,6 +42,7 @@ internal class StaticData(override val context: Context): ContextUtils {
|
||||
throw IllegalArgumentException("Global '$name' already exists")
|
||||
}
|
||||
|
||||
// Globals created with this API are *not* thread local.
|
||||
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
|
||||
|
||||
if (!isExported) {
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ internal val ContextUtils.theUnitInstanceRef: ConstPointer
|
||||
get() {
|
||||
val unitDescriptor = context.builtIns.unit
|
||||
return if (isExternal(unitDescriptor)) {
|
||||
constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
|
||||
constPointer(importGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
|
||||
} else {
|
||||
context.llvmDeclarations.getUnitInstanceRef()
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
//--- Setup args --------------------------------------------------------------//
|
||||
|
||||
OBJ_GETTER(setupArgs, int argc, char** argv) {
|
||||
OBJ_GETTER(setupArgs, int argc, const char** argv) {
|
||||
// The count is one less, because we skip argv[0] which is the binary name.
|
||||
ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, argc - 1, OBJ_RESULT);
|
||||
ArrayHeader* array = result->array();
|
||||
@@ -36,7 +36,7 @@ OBJ_GETTER(setupArgs, int argc, char** argv) {
|
||||
//--- main --------------------------------------------------------------------//
|
||||
extern "C" KInt Konan_start(const ObjHeader*);
|
||||
|
||||
extern "C" int Konan_main(int argc, char** argv) {
|
||||
extern "C" int Konan_main(int argc, const char** argv) {
|
||||
RuntimeState* state = InitRuntime();
|
||||
|
||||
if (state == nullptr) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef KOTLIN_KONAN_H
|
||||
#define KOTLIN_KONAN_H
|
||||
|
||||
// To embed Kotlin/Native into a C/C++ application this API could be used.
|
||||
// Compile Kotlin/Native application as usual, but supply -nomain kotlinc
|
||||
// switch, and provide bitcode with real entry point code as -nativelibrary
|
||||
// command line argument.
|
||||
extern "C" int Konan_main(int argc, const char** argv);
|
||||
|
||||
#endif // KOTLIN_KONAN_H
|
||||
@@ -87,7 +87,7 @@ struct MemoryState {
|
||||
namespace {
|
||||
|
||||
// TODO: remove this global.
|
||||
MemoryState* memoryState = nullptr;
|
||||
__thread MemoryState* memoryState = nullptr;
|
||||
|
||||
// TODO: use those allocators for STL containers as well.
|
||||
template <typename T>
|
||||
@@ -264,8 +264,6 @@ ContainerHeader* AllocContainer(size_t size) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FreeContainer(ContainerHeader* header) {
|
||||
RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed");
|
||||
#if TRACE_MEMORY
|
||||
|
||||
@@ -17,12 +17,19 @@
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Runtime.h"
|
||||
|
||||
struct RuntimeState {
|
||||
MemoryState* memoryState;
|
||||
};
|
||||
|
||||
typedef void (*Initializer)();
|
||||
struct InitNode {
|
||||
Initializer init;
|
||||
InitNode* next;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
InitNode* initHeadNode = nullptr;
|
||||
@@ -42,7 +49,7 @@ void InitGlobalVariables() {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void AppendToInitializersTail(struct InitNode *next) {
|
||||
void AppendToInitializersTail(InitNode *next) {
|
||||
// TODO: use RuntimeState.
|
||||
if (initHeadNode == nullptr) {
|
||||
initHeadNode = next;
|
||||
|
||||
@@ -17,25 +17,19 @@
|
||||
#ifndef RUNTIME_RUNTIME_H
|
||||
#define RUNTIME_RUNTIME_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
struct RuntimeState;
|
||||
struct InitNode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*Initializer)();
|
||||
struct InitNode {
|
||||
Initializer init;
|
||||
struct InitNode* next;
|
||||
};
|
||||
|
||||
void AppendToInitializersTail(struct InitNode*);
|
||||
|
||||
RuntimeState* InitRuntime();
|
||||
void DeinitRuntime(RuntimeState* state);
|
||||
|
||||
// Appends given node to an initializer list.
|
||||
void AppendToInitializersTail(struct InitNode*);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import MessageChannel.*
|
||||
|
||||
val nameToWorker = mapOf("worker1" to ::Worker1, "worker2" to ::Worker2)
|
||||
|
||||
fun StartWorker(target: String, name: String, vararg args: String): worker_id_t {
|
||||
return memScoped {
|
||||
val workerArgs = arrayOf(name, *args)
|
||||
CreateWorker(target, workerArgs.size, workerArgs.map { it.cstr.getPointer(memScope) }.toCValues())
|
||||
}
|
||||
}
|
||||
|
||||
fun CheckWorker(args: Array<String>) : Boolean {
|
||||
if (args.size == 0) return false
|
||||
val handler = nameToWorker[args[0]]
|
||||
if (handler == null) return false
|
||||
handler(args)
|
||||
return true
|
||||
}
|
||||
|
||||
fun Worker1(args: Array<String>) {
|
||||
println("I am Worker1 passed ${args[1]}")
|
||||
memScoped {
|
||||
val message = CreateMessage(10)!!
|
||||
var main_thread : Int
|
||||
while (true) {
|
||||
val result = GetMessage(message, -1)
|
||||
if (result == 0) {
|
||||
main_thread = message.pointed.source_
|
||||
println("${args[1]} got message ${message.pointed.kind_} ${message.pointed.source_}->${message.pointed.destination_}")
|
||||
if (message.pointed.kind_ == 42) break
|
||||
}
|
||||
}
|
||||
message.pointed.kind_ = 1
|
||||
SendMessage(main_thread, message)
|
||||
}
|
||||
}
|
||||
|
||||
fun Worker2(args: Array<String>) {
|
||||
println("I am Worker2 passed ${args[1]}")
|
||||
memScoped {
|
||||
val message = CreateMessage(10)!!
|
||||
var main_thread : Int
|
||||
while (true) {
|
||||
val result = GetMessage(message, -1)
|
||||
if (result == 0) {
|
||||
main_thread = message.pointed.source_
|
||||
println("${args[1]} got message ${message.pointed.kind_} ${message.pointed.source_}->${message.pointed.destination_}")
|
||||
if (message.pointed.kind_ == 42) break
|
||||
}
|
||||
}
|
||||
message.pointed.kind_ = 2
|
||||
SendMessage(main_thread, message)
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val id = if (args.size > 0) args[0] else "main"
|
||||
println("Hi from $id")
|
||||
if (CheckWorker(args)) return
|
||||
|
||||
memScoped {
|
||||
val worker1 = StartWorker("thread", "worker1", "Foo")
|
||||
val worker2 = StartWorker("thread", "worker2", "Bar")
|
||||
|
||||
val message1 = CreateMessage(20)!!
|
||||
val message2 = CreateMessage(20)!!
|
||||
message1.pointed.kind_ = 1239
|
||||
message2.pointed.kind_ = 1566
|
||||
|
||||
for (i in 1 .. 200) {
|
||||
SendMessage(worker1, message1)
|
||||
message1.pointed.kind_++
|
||||
SendMessage(worker2, message2)
|
||||
message2.pointed.kind_--
|
||||
}
|
||||
|
||||
// Send termination message.
|
||||
message1.pointed.kind_ = 42
|
||||
SendMessage(worker1, message1)
|
||||
message2.pointed.kind_ = 42
|
||||
SendMessage(worker2, message2)
|
||||
|
||||
// Wait for termination acknowledgment.
|
||||
for (i in 1 .. 2) {
|
||||
val result = GetMessage(message1, 1000)
|
||||
if (result == 0) {
|
||||
println("main got pingpack ${message1.pointed.kind_}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "MessageChannel.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Konan entry point.
|
||||
extern "C" int Konan_main(int argc, const char** argv);
|
||||
|
||||
struct WorkerState {
|
||||
WorkerState(worker_id_t id, int argc, const char** argv)
|
||||
: id_(id), argc_(argc + 1), argv_(nullptr) {
|
||||
printf("create with %d\n", id);
|
||||
|
||||
argv_ = reinterpret_cast<char**>(malloc(sizeof(char*) * argc_));
|
||||
argv_[0] = strdup("worker");
|
||||
for (auto i = 0; i < argc; i++) {
|
||||
argv_[i + 1] = strdup(argv[i]);
|
||||
}
|
||||
pthread_mutex_init(&mutex_, nullptr);
|
||||
pthread_cond_init(&cond_, nullptr);
|
||||
blocked_on_message_ = nullptr;
|
||||
blocked_message_used_ = false;
|
||||
}
|
||||
|
||||
~WorkerState() {
|
||||
for (auto i = 0; i < argc_; i++) {
|
||||
free(argv_[i]);
|
||||
}
|
||||
if (argv_) free(argv_);
|
||||
|
||||
pthread_mutex_destroy(&mutex_);
|
||||
pthread_cond_destroy(&cond_);
|
||||
}
|
||||
|
||||
int GetMessageNotEmptyLocked(Message* message);
|
||||
|
||||
bool HasMessageLocked() const {
|
||||
return (blocked_on_message_ != nullptr && blocked_message_used_) ||
|
||||
!queue_.empty();
|
||||
}
|
||||
|
||||
std::deque<Message*> queue_;
|
||||
Message* blocked_on_message_;
|
||||
bool blocked_message_used_;
|
||||
pthread_mutex_t mutex_;
|
||||
pthread_cond_t cond_;
|
||||
worker_id_t id_;
|
||||
pthread_t tid_;
|
||||
int argc_;
|
||||
char** argv_;
|
||||
};
|
||||
|
||||
__thread WorkerState* current_worker = nullptr;
|
||||
|
||||
struct WorkersState {
|
||||
WorkersState() : current_worker_id_(1) {
|
||||
pthread_mutex_init(&mutex_, nullptr);
|
||||
auto thisWorker = new WorkerState(current_worker_id_++, 0, nullptr);
|
||||
AddWorker(pthread_self(), thisWorker);
|
||||
current_worker = thisWorker;
|
||||
}
|
||||
|
||||
~WorkersState() {
|
||||
for (auto worker : workers_) delete worker;
|
||||
pthread_mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
void AddWorkerLocked(pthread_t tid, WorkerState* state) {
|
||||
workers_.insert(state);
|
||||
state->tid_ = tid;
|
||||
id_map_[state->id_] = state;
|
||||
thread_map_[state->tid_] = state;
|
||||
}
|
||||
|
||||
void AddWorker(pthread_t tid, WorkerState* state) {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
AddWorkerLocked(tid, state);
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
void RemoveWorkerLocked(WorkerState* worker) {
|
||||
workers_.erase(worker);
|
||||
id_map_.erase(worker->id_);
|
||||
thread_map_.erase(worker->tid_);
|
||||
delete worker;
|
||||
}
|
||||
|
||||
void RemoveWorker(WorkerState* worker) {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
RemoveWorkerLocked(worker);
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
pthread_mutex_t mutex_;
|
||||
worker_id_t current_worker_id_;
|
||||
std::unordered_map<worker_id_t, WorkerState*> id_map_;
|
||||
std::unordered_map<pthread_t, WorkerState*> thread_map_;
|
||||
std::unordered_set<WorkerState*> workers_;
|
||||
};
|
||||
|
||||
WorkersState* getWorkers() {
|
||||
static WorkersState* instance = new WorkersState();
|
||||
return instance;
|
||||
}
|
||||
|
||||
void copyMessageSteal(Message* destination, Message* source) {
|
||||
destination->kind_ = source->kind_;
|
||||
if (source->data_size_ > 0) {
|
||||
free(destination->data_);
|
||||
destination->data_ = source->data_;
|
||||
destination->data_capacity_ = source->data_capacity_;
|
||||
source->data_ = nullptr;
|
||||
}
|
||||
destination->data_size_ = source->data_size_;
|
||||
}
|
||||
|
||||
void copyMessageNoSteal(Message* destination, const Message* source) {
|
||||
destination->kind_ = source->kind_;
|
||||
if (destination->data_capacity_ < source->data_size_) {
|
||||
if (destination->data_) free(destination->data_);
|
||||
destination->data_ = malloc(source->data_size_);
|
||||
destination->data_capacity_ = source->data_size_;
|
||||
}
|
||||
memcpy(destination->data_, source->data_, source->data_size_);
|
||||
destination->data_size_ = source->data_size_;
|
||||
}
|
||||
|
||||
void* ThreadRunner(void* arg) {
|
||||
WorkerState* state = reinterpret_cast<WorkerState*>(arg);
|
||||
current_worker = state;
|
||||
Konan_main(state->argc_, const_cast<const char**>(state->argv_));
|
||||
getWorkers()->RemoveWorker(current_worker);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void copyOrEnqueueMessageLocked(WorkerState* sender, WorkerState* receiver, const Message* message) {
|
||||
bool use_blocked = receiver->blocked_on_message_ != nullptr &&
|
||||
!receiver->blocked_message_used_;
|
||||
Message* result =
|
||||
use_blocked ? receiver->blocked_on_message_ : CreateMessage(message->data_size_);
|
||||
result->source_ = sender->id_;
|
||||
result->destination_ = receiver->id_;
|
||||
copyMessageNoSteal(result, message);
|
||||
if (use_blocked)
|
||||
receiver->blocked_message_used_ = true;
|
||||
else
|
||||
receiver->queue_.push_back(result);
|
||||
}
|
||||
|
||||
int WorkerState::GetMessageNotEmptyLocked(Message* result) {
|
||||
bool blocked_used = blocked_on_message_ != nullptr && blocked_message_used_;
|
||||
if (blocked_used) {
|
||||
assert(result == blocked_on_message_);
|
||||
blocked_on_message_ = nullptr;
|
||||
blocked_message_used_ = false;
|
||||
return 0;
|
||||
}
|
||||
Message* from_queue = queue_.front();
|
||||
result->source_ = from_queue->source_;
|
||||
result->destination_ = from_queue->destination_;
|
||||
copyMessageSteal(result, from_queue);
|
||||
|
||||
queue_.pop_front();
|
||||
ReleaseMessage(from_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Message* CreateMessage(int64_t data_capacity) {
|
||||
auto result = reinterpret_cast<Message*>(calloc(1, sizeof(Message)));
|
||||
result->data_size_ = 0;
|
||||
if (data_capacity == 0) {
|
||||
result->data_ = nullptr;
|
||||
} else {
|
||||
result->data_ = malloc(data_capacity);
|
||||
}
|
||||
result->data_capacity_ = data_capacity;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReleaseMessage(Message* message) {
|
||||
if (message->data_ != nullptr) free(message->data_);
|
||||
free(message);
|
||||
}
|
||||
|
||||
worker_id_t CreateWorker(const char* where, int argc, const char** argv) {
|
||||
auto workers = getWorkers();
|
||||
worker_id_t id = INVALID_WORKER;
|
||||
pthread_mutex_lock(&workers->mutex_);
|
||||
|
||||
WorkerState* state = new WorkerState(workers->current_worker_id_++, argc, argv);
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
int rc = pthread_create(&tid, &attr, ThreadRunner, state);
|
||||
if (rc != 0) {
|
||||
printf("pthread_create(): %d\n", rc);
|
||||
delete state;
|
||||
} else {
|
||||
workers->AddWorkerLocked(tid, state);
|
||||
id = state->id_;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&workers->mutex_);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
worker_id_t CurrentWorker() {
|
||||
return current_worker->id_;
|
||||
}
|
||||
|
||||
int SendMessage(worker_id_t destination_id, const Message* message) {
|
||||
auto workers = getWorkers();
|
||||
int rc = 0;
|
||||
pthread_mutex_lock(&workers->mutex_);
|
||||
auto destination = workers->id_map_[destination_id];
|
||||
if (destination != nullptr) {
|
||||
pthread_mutex_lock(&destination->mutex_);
|
||||
copyOrEnqueueMessageLocked(current_worker, destination, message);
|
||||
pthread_cond_signal(&destination->cond_);
|
||||
pthread_mutex_unlock(&destination->mutex_);
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
pthread_mutex_unlock(&workers->mutex_);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int GetMessage(Message* message, int timeout_ms) {
|
||||
auto worker = current_worker;
|
||||
int result = -1;
|
||||
pthread_mutex_lock(&worker->mutex_);
|
||||
if (!worker->queue_.empty()) {
|
||||
result = worker->GetMessageNotEmptyLocked(message);
|
||||
} else {
|
||||
worker->blocked_on_message_ = message;
|
||||
worker->blocked_message_used_ = false;
|
||||
if (timeout_ms < 0) {
|
||||
// Infinite wait.
|
||||
while (!worker->HasMessageLocked()) {
|
||||
int rc = pthread_cond_wait(&worker->cond_, &worker->mutex_);
|
||||
if (rc != 0) break;
|
||||
}
|
||||
if (worker->HasMessageLocked()) {
|
||||
result = worker->GetMessageNotEmptyLocked(message);
|
||||
}
|
||||
} else if (timeout_ms > 0) {
|
||||
// Timed wait.
|
||||
struct timespec ts;
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
ts.tv_sec = tp.tv_sec;
|
||||
ts.tv_nsec = tp.tv_usec * 1000 + timeout_ms * 1000000;
|
||||
ts.tv_sec += ts.tv_nsec / 1000000000;
|
||||
ts.tv_nsec %= 1000000000;
|
||||
while (!worker->HasMessageLocked()) {
|
||||
int rc = pthread_cond_timedwait(&worker->cond_, &worker->mutex_, &ts);
|
||||
if (rc != 0) break;
|
||||
}
|
||||
if (worker->HasMessageLocked()) {
|
||||
result = worker->GetMessageNotEmptyLocked(message);
|
||||
}
|
||||
}
|
||||
worker->blocked_on_message_ = nullptr;
|
||||
worker->blocked_message_used_ = false;
|
||||
}
|
||||
pthread_mutex_unlock(&worker->mutex_);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
headers = MessageChannel.h
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MESSAGE_CHANNEL_H
|
||||
#define MESSAGE_CHANNEL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int32_t worker_id_t;
|
||||
typedef int32_t message_kind_t;
|
||||
typedef struct {
|
||||
worker_id_t source_;
|
||||
worker_id_t destination_;
|
||||
message_kind_t kind_;
|
||||
void* data_;
|
||||
int64_t data_size_;
|
||||
int64_t data_capacity_;
|
||||
} Message;
|
||||
|
||||
#define INVALID_WORKER -1
|
||||
|
||||
// Creates new worker, return its id, or INVALID_WORKER if can not.
|
||||
worker_id_t CreateWorker(const char* where, int argc, const char** argv);
|
||||
// Gets id of the current worker.
|
||||
worker_id_t CurrentWorker();
|
||||
|
||||
// Create message.
|
||||
Message* CreateMessage(int64_t data_size);
|
||||
void ReleaseMessage(Message* message);
|
||||
|
||||
// Returns 0 is message was delivered to 'destination' event queue,
|
||||
// and -1 if destination is invalid or cannot accept more messages.
|
||||
int SendMessage(worker_id_t destination, const Message* message);
|
||||
|
||||
// Gets next message, returns -1 if no message arrived until timeout
|
||||
// expired. 'timeout_ms' can have following values:
|
||||
// * > 0 - number of milliseconds to wait
|
||||
// * == 0 - return immediately if there's a message already
|
||||
// * < 0 - wait forever, until event arrives or program terminates
|
||||
int GetMessage(Message* message, int timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // MESSAGE_CHANNEL_H
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DIR=.
|
||||
PATH=../../dist/bin:../../bin:$PATH
|
||||
DEPS=$(dirname `type -p konanc`)/../dependencies
|
||||
|
||||
if [ x$TARGET == x ]; then
|
||||
case "$OSTYPE" in
|
||||
darwin*) TARGET=macbook ;;
|
||||
linux*) TARGET=linux ;;
|
||||
*) echo "unknown: $OSTYPE" && exit 1;;
|
||||
esac
|
||||
fi
|
||||
|
||||
CLANG_linux=$DEPS/clang-llvm-3.9.0-linux-x86-64/bin/clang++
|
||||
CLANG_macbook=$DEPS/clang-llvm-3.9.0-darwin-macos/bin/clang++
|
||||
|
||||
var=CFLAGS_${TARGET}
|
||||
CFLAGS=${!var}
|
||||
var=LINKER_ARGS_${TARGET}
|
||||
LINKER_ARGS=${!var}
|
||||
var=COMPILER_ARGS_${TARGET}
|
||||
COMPILER_ARGS=${!var} # add -opt for an optimized build.
|
||||
var=CLANG_${TARGET}
|
||||
CLANG=${!var}
|
||||
|
||||
$CLANG -std=c++11 -c $DIR/MessageChannel.cpp -o $DIR/MessageChannel.bc -emit-llvm || exit 1
|
||||
cinterop -def $DIR/MessageChannel.def -copt "-I$DIR" -target $TARGET -o $DIR/MessageChannel.kt.bc || exit 1
|
||||
konanc $DIR/Concurrent.kt -library $DIR/MessageChannel.kt.bc \
|
||||
-nativelibrary $DIR/MessageChannel.bc -o Concurrent.kexe || exit 1
|
||||
@@ -23,7 +23,7 @@ fun main(args: Array<String>) {
|
||||
return
|
||||
}
|
||||
|
||||
val port = atoi(args[0]).toShort()
|
||||
val port = args[0].toShort()
|
||||
|
||||
memScoped {
|
||||
|
||||
|
||||
@@ -957,15 +957,15 @@ fun main(args: Array<String>) {
|
||||
var width = 10
|
||||
var height = 20
|
||||
when (args.size) {
|
||||
1 -> startLevel = atoi(args[0])
|
||||
1 -> startLevel = args[0].toInt()
|
||||
2 -> {
|
||||
width = atoi(args[0])
|
||||
height = atoi(args[1])
|
||||
width = args[0].toInt()
|
||||
height = args[1].toInt()
|
||||
}
|
||||
3 -> {
|
||||
width = atoi(args[0])
|
||||
height = atoi(args[1])
|
||||
startLevel = atoi(args[2])
|
||||
width = args[0].toInt()
|
||||
height = args[1].toInt()
|
||||
startLevel = args[2].toInt()
|
||||
}
|
||||
}
|
||||
val visualizer = SDL_Visualizer(width, height)
|
||||
|
||||
Reference in New Issue
Block a user