[vmkit-commits] [vmkit] r77588 - in /vmkit/trunk: include/mvm/Threads/Thread.h lib/JnJVM/Compiler/JnjvmModule.cpp lib/JnJVM/LLVMRuntime/runtime-default.ll lib/JnJVM/VMCore/JavaThread.cpp lib/JnJVM/VMCore/JavaThread.h lib/Mvm/CommonThread/ctlock.cpp lib/Mvm/CommonThread/ctthread.cpp lib/Mvm/GCMmap2/MvmGC.h lib/Mvm/GCMmap2/gc.cpp lib/Mvm/GCMmap2/gccollector.cpp lib/Mvm/GCMmap2/gcthread.cpp lib/Mvm/GCMmap2/gcthread.h lib/Mvm/Runtime/Object.cpp
Nicolas Geoffray
nicolas.geoffray at lip6.fr
Thu Jul 30 02:38:40 PDT 2009
Author: geoffray
Date: Thu Jul 30 04:38:29 2009
New Revision: 77588
URL: http://llvm.org/viewvc/llvm-project?rev=77588&view=rev
Log:
Fully support a cooperative GC.
Modified:
vmkit/trunk/include/mvm/Threads/Thread.h
vmkit/trunk/lib/JnJVM/Compiler/JnjvmModule.cpp
vmkit/trunk/lib/JnJVM/LLVMRuntime/runtime-default.ll
vmkit/trunk/lib/JnJVM/VMCore/JavaThread.cpp
vmkit/trunk/lib/JnJVM/VMCore/JavaThread.h
vmkit/trunk/lib/Mvm/CommonThread/ctlock.cpp
vmkit/trunk/lib/Mvm/CommonThread/ctthread.cpp
vmkit/trunk/lib/Mvm/GCMmap2/MvmGC.h
vmkit/trunk/lib/Mvm/GCMmap2/gc.cpp
vmkit/trunk/lib/Mvm/GCMmap2/gccollector.cpp
vmkit/trunk/lib/Mvm/GCMmap2/gcthread.cpp
vmkit/trunk/lib/Mvm/GCMmap2/gcthread.h
vmkit/trunk/lib/Mvm/Runtime/Object.cpp
Modified: vmkit/trunk/include/mvm/Threads/Thread.h
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/include/mvm/Threads/Thread.h?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/include/mvm/Threads/Thread.h (original)
+++ vmkit/trunk/include/mvm/Threads/Thread.h Thu Jul 30 04:38:29 2009
@@ -10,7 +10,7 @@
#ifndef MVM_THREAD_H
#define MVM_THREAD_H
-#include <sched.h>
+#include <stdlib.h>
#include "types.h"
@@ -103,9 +103,7 @@
/// yield - Yield the processor to another thread.
///
- static void yield(void) {
- sched_yield();
- }
+ static void yield(void);
/// kill - Kill the thread with the given pid by sending it a signal.
///
@@ -143,6 +141,15 @@
///
bool doYield;
+ /// inGC - Flag to tell that the thread is being part of a GC.
+ ///
+ bool inGC;
+
+ /// stackScanned - Flag to tell that the thread's stack has already
+ /// been analyzed.
+ ///
+ bool stackScanned;
+
/// get - Get the thread specific data of the current thread.
///
static Thread* get() {
@@ -151,6 +158,12 @@
private:
+ /// lastSP - If the thread is running native code that can not be
+ /// interrupted, lastSP is not null and contains the value of the
+ /// stack pointer before entering native.
+ ///
+ void* lastSP;
+
/// internalThreadID - The implementation specific thread id.
///
void* internalThreadID;
@@ -164,6 +177,10 @@
///
virtual void internalClearException() {}
+ /// joinCollection - Join a collection.
+ ///
+ void joinCollection();
+
public:
/// ~Thread - Give the class a home.
@@ -174,6 +191,23 @@
/// a tracer.
///
virtual void tracer() {}
+
+ void* getLastSP() { return lastSP; }
+ void setLastSP(void* V) { lastSP = V; }
+
+ void enterUncooperativeCode() {
+ if (isMvmThread()) {
+ lastSP = __builtin_frame_address(0);
+ if (doYield && !inGC) joinCollection();
+ }
+ }
+
+ void leaveUncooperativeCode() {
+ if (isMvmThread()) {
+ lastSP = 0;
+ if (doYield && !inGC) joinCollection();
+ }
+ }
/// clearException - Clear any pending exception of the current thread.
@@ -181,10 +215,23 @@
internalClearException();
}
+ bool isMvmThread() {
+ if (!baseAddr) return false;
+ else return (((uintptr_t)this) & MvmThreadMask) == baseAddr;
+ }
+
+ /// baseAddr - The base address for all threads.
+ static uintptr_t baseAddr;
+
/// IDMask - Apply this mask to the stack pointer to get the Thread object.
///
static const uint64_t IDMask = 0x7FF00000;
+ /// MvmThreadMask - Apply this mask to verify that the current thread was
+ /// created by Mvm.
+ ///
+ static const uint64_t MvmThreadMask = 0xF0000000;
+
/// OverflowMask - Apply this mask to implement overflow checks. For
/// efficiency, we lower the available size of the stack: it can never go
/// under 0xC0000
Modified: vmkit/trunk/lib/JnJVM/Compiler/JnjvmModule.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/JnJVM/Compiler/JnjvmModule.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/JnJVM/Compiler/JnjvmModule.cpp (original)
+++ vmkit/trunk/lib/JnJVM/Compiler/JnjvmModule.cpp Thu Jul 30 04:38:29 2009
@@ -199,9 +199,9 @@
OffsetIsolateInThreadConstant = ConstantInt::get(Type::Int32Ty, 3);
OffsetDoYieldInThreadConstant = ConstantInt::get(Type::Int32Ty, 6);
- OffsetJNIInThreadConstant = ConstantInt::get(Type::Int32Ty, 9);
- OffsetJavaExceptionInThreadConstant = ConstantInt::get(Type::Int32Ty, 10);
- OffsetCXXExceptionInThreadConstant = ConstantInt::get(Type::Int32Ty, 11);
+ OffsetJNIInThreadConstant = ConstantInt::get(Type::Int32Ty, 12);
+ OffsetJavaExceptionInThreadConstant = ConstantInt::get(Type::Int32Ty, 13);
+ OffsetCXXExceptionInThreadConstant = ConstantInt::get(Type::Int32Ty, 14);
ClassReadyConstant = ConstantInt::get(Type::Int8Ty, ready);
Modified: vmkit/trunk/lib/JnJVM/LLVMRuntime/runtime-default.ll
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/JnJVM/LLVMRuntime/runtime-default.ll?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/JnJVM/LLVMRuntime/runtime-default.ll (original)
+++ vmkit/trunk/lib/JnJVM/LLVMRuntime/runtime-default.ll Thu Jul 30 04:38:29 2009
@@ -41,13 +41,16 @@
;;; Field 4: MyVM
;;; Field 5: baseSP
;;; Field 6: doYield
-;;; Field 7: internalThreadID
-;;; field 8: routine
-;;; field 9: jnienv
-;;; field 10: Java pendingException
-;;; field 11: CXX pendingException
-%JavaThread = type { %VT*, %JavaThread*, %JavaThread*, i8*, i8*, i8*, i1, i8*,
- i8*, i8*, %JavaObject*, i8* }
+;;; Field 7: inGC
+;;; Field 8: stackScanned
+;;; Field 9: lastSP
+;;; Field 10: internalThreadID
+;;; field 11: routine
+;;; field 12: jnienv
+;;; field 13: Java pendingException
+;;; field 14: CXX pendingException
+%JavaThread = type { %VT*, %JavaThread*, %JavaThread*, i8*, i8*, i8*, i1, i1,
+ i1, i8*, i8*, i8*, i8*, %JavaObject*, i8* }
%Attribut = type { %UTF8*, i32, i32 }
Modified: vmkit/trunk/lib/JnJVM/VMCore/JavaThread.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/JnJVM/VMCore/JavaThread.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/JnJVM/VMCore/JavaThread.cpp (original)
+++ vmkit/trunk/lib/JnJVM/VMCore/JavaThread.cpp Thu Jul 30 04:38:29 2009
@@ -103,6 +103,9 @@
assert((addresses.size() % 2) && "Wrong stack");
addresses.push_back(cur);
+
+ // Start uncooperative mode.
+ enterUncooperativeCode();
}
void JavaThread::startJava() {
Modified: vmkit/trunk/lib/JnJVM/VMCore/JavaThread.h
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/JnJVM/VMCore/JavaThread.h?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/JnJVM/VMCore/JavaThread.h (original)
+++ vmkit/trunk/lib/JnJVM/VMCore/JavaThread.h Thu Jul 30 04:38:29 2009
@@ -240,6 +240,9 @@
localJNIRefs->removeJNIReferences(this, *currentAddedReferences);
addresses.pop_back();
+
+ // Go back to cooperative mode.
+ leaveUncooperativeCode();
}
/// endJava - Record that we are leaving Java code.
Modified: vmkit/trunk/lib/Mvm/CommonThread/ctlock.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/CommonThread/ctlock.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/CommonThread/ctlock.cpp (original)
+++ vmkit/trunk/lib/Mvm/CommonThread/ctlock.cpp Thu Jul 30 04:38:29 2009
@@ -63,8 +63,11 @@
}
void LockNormal::lock() {
+ Thread* th = Thread::get();
+ th->enterUncooperativeCode();
pthread_mutex_lock((pthread_mutex_t*)&internalLock);
- owner = mvm::Thread::get();
+ th->leaveUncooperativeCode();
+ owner = th;
}
void LockNormal::unlock() {
@@ -75,8 +78,11 @@
void LockRecursive::lock() {
if (!selfOwner()) {
+ Thread* th = Thread::get();
+ th->enterUncooperativeCode();
pthread_mutex_lock((pthread_mutex_t*)&internalLock);
- owner = mvm::Thread::get();
+ th->leaveUncooperativeCode();
+ owner = th;
}
++n;
}
@@ -113,8 +119,11 @@
if (selfOwner()) {
n += count;
} else {
+ Thread* th = Thread::get();
+ th->enterUncooperativeCode();
pthread_mutex_lock((pthread_mutex_t*)&internalLock);
- owner = mvm::Thread::get();
+ th->leaveUncooperativeCode();
+ owner = th;
n = count;
}
}
@@ -136,8 +145,11 @@
int n = l->unsafeUnlock();
+ Thread* th = Thread::get();
+ th->enterUncooperativeCode();
int res = pthread_cond_wait((pthread_cond_t*)&internalCond,
(pthread_mutex_t*)&(l->internalLock));
+ th->leaveUncooperativeCode();
assert(!res && "Error on wait");
l->unsafeLock(n);
@@ -158,9 +170,12 @@
int n = l->unsafeUnlock();
+ Thread* th = Thread::get();
+ th->enterUncooperativeCode();
int res = pthread_cond_timedwait((pthread_cond_t*)&internalCond,
(pthread_mutex_t*)&(l->internalLock),
&timeout);
+ th->leaveUncooperativeCode();
assert((!res || res == ETIMEDOUT) && "Error on timed wait");
l->unsafeLock(n);
Modified: vmkit/trunk/lib/Mvm/CommonThread/ctthread.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/CommonThread/ctthread.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/CommonThread/ctthread.cpp (original)
+++ vmkit/trunk/lib/Mvm/CommonThread/ctthread.cpp Thu Jul 30 04:38:29 2009
@@ -19,6 +19,7 @@
#include <ctime>
#include <pthread.h>
#include <sys/mman.h>
+#include <sched.h>
#include <unistd.h>
using namespace mvm;
@@ -35,6 +36,21 @@
pthread_exit((void*)value);
}
+void Thread::yield(void) {
+ Thread* th = mvm::Thread::get();
+ if (th->isMvmThread()) {
+ if (th->doYield && !th->inGC) th->joinCollection();
+ }
+ sched_yield();
+}
+
+void Thread::joinCollection() {
+ Collector::traceStackThread();
+}
+
+
+uintptr_t Thread::baseAddr = 0;
+
// These could be set at runtime.
#define STACK_SIZE 0x100000
#define NR_THREADS 255
@@ -91,6 +107,7 @@
memset((void*)used, 0, NR_THREADS * sizeof(uint32));
allocPtr = 0;
+ mvm::Thread::baseAddr = baseAddr;
}
uintptr_t allocate() {
Modified: vmkit/trunk/lib/Mvm/GCMmap2/MvmGC.h
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/GCMmap2/MvmGC.h?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/GCMmap2/MvmGC.h (original)
+++ vmkit/trunk/lib/Mvm/GCMmap2/MvmGC.h Thu Jul 30 04:38:29 2009
@@ -81,6 +81,8 @@
static inline size_t real_nbb(GCChunkNode *n) {
return n->nbb() - sizeof(gcRoot);
}
+
+ static void traceForeignThreadStack(mvm::Thread* th, void* endPtr);
public:
static GCThread *threads; /* le gestionnaire de thread et de synchro */
@@ -98,6 +100,10 @@
static int siggc();
+ static void traceStackThread() {
+ siggc_handler(0);
+ }
+
static void inject_my_thread(mvm::Thread* th);
static inline void remove_my_thread(mvm::Thread* th) {
threads->remove(th);
Modified: vmkit/trunk/lib/Mvm/GCMmap2/gc.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/GCMmap2/gc.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/GCMmap2/gc.cpp (original)
+++ vmkit/trunk/lib/Mvm/GCMmap2/gc.cpp Thu Jul 30 04:38:29 2009
@@ -39,16 +39,63 @@
void Collector::siggc_handler(int) {
mvm::Thread* th = mvm::Thread::get();
+ th->inGC = true;
- jmp_buf buf;
- setjmp(buf);
Collector::threads->stackLock();
+
+ if (Collector::threads->cooperative && !th->doYield) {
+ // I was previously blocked, and I'm running late: someone else collected
+ // my stack, and the GC has finished already. Just unlock and return.
+ Collector::threads->stackUnlock();
+ th->inGC = false;
+ return;
+ }
+
+ // I woke up while a GC was happening, and no-one has collected my stack yet.
+ // Do it now.
+ if (!th->stackScanned) {
+ jmp_buf buf;
+ setjmp(buf);
- if(!th) /* The thread is being destroyed */
- Collector::threads->another_mark();
- else {
- register unsigned int **cur = (unsigned int**)(void*)&buf;
+ if(!th) /* The thread is being destroyed */
+ Collector::threads->another_mark();
+ else {
+ register unsigned int **cur = (unsigned int**)(void*)&buf;
+ register unsigned int **max = (unsigned int**)th->baseSP;
+
+ GCChunkNode *node;
+
+ for(; cur<max; cur++) {
+ if((node = o2node(*cur)) && (!Collector::isMarked(node))) {
+ node->remove();
+ node->append(Collector::used_nodes);
+ Collector::mark(node);
+ }
+ }
+
+ Collector::threads->another_mark();
+ }
+ th->stackScanned = true;
+ }
+
+ // Wait for the collection to finish.
+ Collector::threads->waitCollection();
+ Collector::threads->stackUnlock();
+
+ // If the current thread is not the collector thread, this means that the
+ // collection is finished. Set inGC to false.
+ if(th != threads->getCurrentCollector())
+ th->inGC = false;
+}
+
+void Collector::traceForeignThreadStack(mvm::Thread* th, void* endPtr) {
+ Collector::threads->stackLock();
+
+ // The thread may have waken up during this GC. In this case, it may also
+ // have collected its stack. Don't scan it then.
+ if (!th->stackScanned) {
+ register unsigned int **cur = (unsigned int**)endPtr;
register unsigned int **max = (unsigned int**)th->baseSP;
GCChunkNode *node;
@@ -60,9 +107,13 @@
Collector::mark(node);
}
}
-
Collector::threads->another_mark();
- Collector::threads->waitCollection();
+ th->stackScanned = true;
}
+
Collector::threads->stackUnlock();
}
+
+extern "C" void conditionalSafePoint() {
+ Collector::traceStackThread();
+}
Modified: vmkit/trunk/lib/Mvm/GCMmap2/gccollector.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/GCMmap2/gccollector.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/GCMmap2/gccollector.cpp (original)
+++ vmkit/trunk/lib/Mvm/GCMmap2/gccollector.cpp Thu Jul 30 04:38:29 2009
@@ -46,6 +46,7 @@
mvm::Thread* th = mvm::Thread::get();
th->MyVM->startCollection();
+ th->inGC = true;
threads->synchronize();
@@ -103,6 +104,7 @@
next = cur->next();
allocator->reject_chunk(cur);
}
+ th->inGC = false;
}
Modified: vmkit/trunk/lib/Mvm/GCMmap2/gcthread.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/GCMmap2/gcthread.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/GCMmap2/gcthread.cpp (original)
+++ vmkit/trunk/lib/Mvm/GCMmap2/gcthread.cpp Thu Jul 30 04:38:29 2009
@@ -20,18 +20,58 @@
}
void GCThread::synchronize() {
- int signo = Collector::siggc();
- mvm::Thread* self = mvm::Thread::get();
- assert(self && "No thread local data for this thread");
- current_collector = self;
- _nb_collected = 0;
-
- for(mvm::Thread* cur = (mvm::Thread*)self->next(); cur != self;
- cur = (mvm::Thread*)cur->next()) {
- cur->kill(signo);
- }
+
+ if (cooperative) {
+ mvm::Thread* self = mvm::Thread::get();
+ assert(self && "No thread local data for this thread");
+ current_collector = self;
+ _nb_collected = 0;
+
+ // Lock stacks. Changes on the doYield flag of threads must be
+ // protected, as threads may wake up after being blocked in native code
+ // and join the collection.
+ stackLock();
+
+ mvm::Thread* cur = self;
+ do {
+ cur->stackScanned = false;
+ cur->doYield = true;
+ cur = (mvm::Thread*)cur->next();
+ } while (cur != self);
+
+ // Unlock now. Each running thread will scan its stack.
+ stackUnlock();
+
+ // Scan the stacks of currently blocked threads.
+ for (mvm::Thread* cur = (mvm::Thread*)self->next(); cur != self;
+ cur = (mvm::Thread*)cur->next()) {
+ void* val = cur->getLastSP();
+ // If val is null, this means that the thread woke up, and is
+ // joining the collection. We are sure the thread will scan its stack.
+ if (val) Collector::traceForeignThreadStack(cur, val);
+ }
+
+ // Finally, scan my stack too!
+ Collector::siggc_handler(0);
+
+ // And wait for other threads to finish.
+ waitStacks();
+ } else {
+ int signo = Collector::siggc();
+ mvm::Thread* self = mvm::Thread::get();
+ self->stackScanned = false;
+ assert(self && "No thread local data for this thread");
+ current_collector = self;
+ _nb_collected = 0;
+
+ for (mvm::Thread* cur = (mvm::Thread*)self->next(); cur != self;
+ cur = (mvm::Thread*)cur->next()) {
+ cur->stackScanned = false;
+ cur->kill(signo);
+ }
- Collector::siggc_handler(signo);
+ Collector::siggc_handler(signo);
- waitStacks();
+ waitStacks();
+ }
}
Modified: vmkit/trunk/lib/Mvm/GCMmap2/gcthread.h
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/GCMmap2/gcthread.h?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/GCMmap2/gcthread.h (original)
+++ vmkit/trunk/lib/Mvm/GCMmap2/gcthread.h Thu Jul 30 04:38:29 2009
@@ -42,12 +42,18 @@
public:
mvm::Thread* base;
-
+ bool cooperative;
+
+ mvm::Thread* getCurrentCollector() {
+ return current_collector;
+ }
+
GCThread() {
_nb_threads = 0;
_nb_collected = 0;
current_collector = 0;
base = 0;
+ cooperative = false;
}
inline unsigned int get_nb_threads() {
@@ -61,7 +67,25 @@
void waitStacks();
void waitCollection();
- inline void collectionFinished() { _collectionCond.broadcast(); }
+
+ inline void collectionFinished() {
+ if (cooperative) {
+ // We lock here to make sure no previously blocked in native threads
+ // will join the collection and never go back to running code.
+ stackLock();
+ mvm::Thread* cur = current_collector;
+ do {
+ cur->doYield = false;
+ cur = (mvm::Thread*)cur->next();
+ } while (cur != current_collector);
+ _collectionCond.broadcast();
+ stackUnlock();
+ } else {
+ _collectionCond.broadcast();
+ }
+ current_collector->inGC = false;
+ }
+
inline void collectorGo() { _stackCond.broadcast(); }
inline void cancel() {
Modified: vmkit/trunk/lib/Mvm/Runtime/Object.cpp
URL: http://llvm.org/viewvc/llvm-project/vmkit/trunk/lib/Mvm/Runtime/Object.cpp?rev=77588&r1=77587&r2=77588&view=diff
==============================================================================
--- vmkit/trunk/lib/Mvm/Runtime/Object.cpp (original)
+++ vmkit/trunk/lib/Mvm/Runtime/Object.cpp Thu Jul 30 04:38:29 2009
@@ -119,6 +119,8 @@
void VirtualMachine::finalizerStart(mvm::Thread* th) {
VirtualMachine* vm = th->MyVM;
+ gc* res = 0;
+ llvm_gcroot(res, 0);
while (true) {
vm->FinalizationLock.lock();
@@ -129,7 +131,6 @@
while (true) {
vm->FinalizationQueueLock.acquire();
- gc* res = 0;
if (vm->CurrentFinalizedIndex != 0) {
res = vm->ToBeFinalized[--vm->CurrentFinalizedIndex];
}
@@ -146,6 +147,7 @@
}
} catch(...) {
}
+ res = 0;
th->clearException();
}
}
@@ -153,6 +155,8 @@
void VirtualMachine::enqueueStart(mvm::Thread* th) {
VirtualMachine* vm = th->MyVM;
+ gc* res = 0;
+ llvm_gcroot(res, 0);
while (true) {
vm->EnqueueLock.lock();
@@ -163,7 +167,6 @@
while (true) {
vm->ToEnqueueLock.acquire();
- gc* res = 0;
if (vm->ToEnqueueIndex != 0) {
res = vm->ToEnqueue[--vm->ToEnqueueIndex];
}
@@ -174,6 +177,7 @@
vm->enqueueReference(res);
} catch(...) {
}
+ res = 0;
th->clearException();
}
}
@@ -307,9 +311,3 @@
void Allocator::freeTemporaryMemory(void* obj) {
return free(obj);
}
-
-
-
-extern "C" void conditionalSafePoint() {
- abort();
-}
More information about the vmkit-commits
mailing list