diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index 06791686ffc..e42a68b8edf 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -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 { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 44d40084652..f8b8cdaade5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -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 } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 144a7489780..193a5485f6f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -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 } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index eb9fe3ff586..98379a9d36e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -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) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index 660545194ed..c5672fb7b88 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -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 } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 428789a5177..7eec5dfa1a0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -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) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt index b516c0fac67..d28c840f45c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt @@ -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) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt index cd54ee57066..875a226286a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt @@ -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() } diff --git a/runtime/src/launcher/cpp/launcher.cpp b/runtime/src/launcher/cpp/launcher.cpp index dc0d2b75f85..d19fb664b3a 100644 --- a/runtime/src/launcher/cpp/launcher.cpp +++ b/runtime/src/launcher/cpp/launcher.cpp @@ -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) { diff --git a/runtime/src/main/cpp/Konan.h b/runtime/src/main/cpp/Konan.h new file mode 100644 index 00000000000..f718beef8ae --- /dev/null +++ b/runtime/src/main/cpp/Konan.h @@ -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 diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 9d107785656..2f9b2c6bdb3 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -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 @@ -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 diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index 0bae57e3a7f..d54317ac44d 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -17,12 +17,19 @@ #include #include +#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; diff --git a/runtime/src/main/cpp/Runtime.h b/runtime/src/main/cpp/Runtime.h index a6253b6496c..1d7605fc636 100644 --- a/runtime/src/main/cpp/Runtime.h +++ b/runtime/src/main/cpp/Runtime.h @@ -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 diff --git a/samples/concurrent/Concurrent.kt b/samples/concurrent/Concurrent.kt new file mode 100644 index 00000000000..9f62a0e3480 --- /dev/null +++ b/samples/concurrent/Concurrent.kt @@ -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) : 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) { + 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) { + 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) { + 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_}") + } + } + } +} diff --git a/samples/concurrent/MessageChannel.cpp b/samples/concurrent/MessageChannel.cpp new file mode 100644 index 00000000000..432a5f73857 --- /dev/null +++ b/samples/concurrent/MessageChannel.cpp @@ -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 +#include +#include +#include + +#include +#include +#include +#include + +#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(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 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 id_map_; + std::unordered_map thread_map_; + std::unordered_set 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(arg); + current_worker = state; + Konan_main(state->argc_, const_cast(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(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 diff --git a/samples/concurrent/MessageChannel.def b/samples/concurrent/MessageChannel.def new file mode 100644 index 00000000000..c9cff668ab0 --- /dev/null +++ b/samples/concurrent/MessageChannel.def @@ -0,0 +1 @@ +headers = MessageChannel.h diff --git a/samples/concurrent/MessageChannel.h b/samples/concurrent/MessageChannel.h new file mode 100644 index 00000000000..24f28be26b1 --- /dev/null +++ b/samples/concurrent/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 + +#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 diff --git a/samples/concurrent/build.sh b/samples/concurrent/build.sh new file mode 100755 index 00000000000..05b8a6378e1 --- /dev/null +++ b/samples/concurrent/build.sh @@ -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 diff --git a/samples/socket/EchoServer.kt b/samples/socket/EchoServer.kt index 812b9d14be7..2b8e5ae6e06 100644 --- a/samples/socket/EchoServer.kt +++ b/samples/socket/EchoServer.kt @@ -23,7 +23,7 @@ fun main(args: Array) { return } - val port = atoi(args[0]).toShort() + val port = args[0].toShort() memScoped { diff --git a/samples/tetris/Tetris.kt b/samples/tetris/Tetris.kt index 4b29184e3a2..8b05e0ce482 100644 --- a/samples/tetris/Tetris.kt +++ b/samples/tetris/Tetris.kt @@ -957,15 +957,15 @@ fun main(args: Array) { 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)