blob: 22f9d98152af7980d03e835003db6f7479e3a007 [file] [log] [blame]
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// 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 <CoreServices/CoreServices.h>
#include <jni.h>
#include <pthread.h>
#include <stdlib.h>
#include <list>
#include <string>
// A structure to pass around the FSEvents info and the list of paths.
struct JNIEventsDiffAwareness {
// FSEvents run loop (thread)
CFRunLoopRef runLoop;
// FSEvents stream reference (reference to the listened stream)
FSEventStreamRef stream;
// List of paths that have been changed since last polling
std::list<std::string> paths;
// Mutex to protect concurrent access of paths.
// FsEventsDiffAwarenessCallback fill that list which is emptied
// by the MacOSXEventsDiffAwareness#poll() method.
// The former is called inside the FsEvents run loop and the latter
// from Java threads.
pthread_mutex_t mutex;
JNIEventsDiffAwareness() { pthread_mutex_init(&mutex, nullptr); }
~JNIEventsDiffAwareness() { pthread_mutex_destroy(&mutex); }
};
// Callback called when an event is reported by the FSEvents API
void FsEventsDiffAwarenessCallback(ConstFSEventStreamRef streamRef,
void *clientCallBackInfo, size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
/* We are just returning the list of modified path but we could return a bit
* more information,
* see
* https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/#//apple_ref/doc/c_ref/FSEventStreamCallback
* If we ever do more, we should be careful because creation and deletion
* event get coaslesced
* into one single entry.
*/
char **paths = static_cast<char **>(eventPaths);
JNIEventsDiffAwareness *info =
static_cast<JNIEventsDiffAwareness *>(clientCallBackInfo);
pthread_mutex_lock(&(info->mutex));
for (int i = 0; i < numEvents; i++) {
info->paths.push_back(std::string(paths[i]));
}
pthread_mutex_unlock(&(info->mutex));
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_skyframe_MacOSXFsEventsDiffAwareness_create(
JNIEnv *env, jobject fsEventsDiffAwareness, jobjectArray paths,
jdouble latency) {
// Create a FSEventStreamContext to pass around (env, fsEventsDiffAwareness)
JNIEventsDiffAwareness *info = new JNIEventsDiffAwareness();
FSEventStreamContext context;
context.version = 0;
context.info = static_cast<void *>(info);
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
// Create an CFArrayRef of CFStringRef from the Java array of String
jsize length = env->GetArrayLength(paths);
CFStringRef *pathsArray = new CFStringRef[length];
for (int i = 0; i < length; i++) {
jstring path = (jstring)env->GetObjectArrayElement(paths, i);
const char *pathCStr = env->GetStringUTFChars(path, NULL);
pathsArray[i] =
CFStringCreateWithCString(NULL, pathCStr, kCFStringEncodingUTF8);
env->ReleaseStringUTFChars(path, pathCStr);
}
CFArrayRef pathsToWatch =
CFArrayCreate(NULL, (const void **)pathsArray, 1, NULL);
delete[] pathsArray;
info->stream = FSEventStreamCreate(
NULL, &FsEventsDiffAwarenessCallback, &context, pathsToWatch,
kFSEventStreamEventIdSinceNow, static_cast<CFAbsoluteTime>(latency),
kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents);
// Save the info pointer to FSEventsDiffAwareness#nativePointer
jbyteArray array = env->NewByteArray(sizeof(info));
env->SetByteArrayRegion(array, 0, sizeof(info),
reinterpret_cast<const jbyte *>(&info));
jclass clazz = env->GetObjectClass(fsEventsDiffAwareness);
jfieldID fid = env->GetFieldID(clazz, "nativePointer", "J");
env->SetLongField(fsEventsDiffAwareness, fid, reinterpret_cast<jlong>(info));
}
JNIEventsDiffAwareness *GetInfo(JNIEnv *env, jobject fsEventsDiffAwareness) {
jclass clazz = env->GetObjectClass(fsEventsDiffAwareness);
jfieldID fid = env->GetFieldID(clazz, "nativePointer", "J");
jlong field = env->GetLongField(fsEventsDiffAwareness, fid);
return reinterpret_cast<JNIEventsDiffAwareness *>(field);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_skyframe_MacOSXFsEventsDiffAwareness_run(
JNIEnv *env, jobject fsEventsDiffAwareness) {
JNIEventsDiffAwareness *info = GetInfo(env, fsEventsDiffAwareness);
info->runLoop = CFRunLoopGetCurrent();
FSEventStreamScheduleWithRunLoop(info->stream, info->runLoop,
kCFRunLoopDefaultMode);
FSEventStreamStart(info->stream);
CFRunLoopRun();
}
extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_google_devtools_build_lib_skyframe_MacOSXFsEventsDiffAwareness_poll(
JNIEnv *env, jobject fsEventsDiffAwareness) {
JNIEventsDiffAwareness *info = GetInfo(env, fsEventsDiffAwareness);
pthread_mutex_lock(&(info->mutex));
jclass classString = env->FindClass("java/lang/String");
jobjectArray result =
env->NewObjectArray(info->paths.size(), classString, NULL);
int i = 0;
for (auto it = info->paths.begin(); it != info->paths.end(); it++, i++) {
env->SetObjectArrayElement(result, i, env->NewStringUTF(it->c_str()));
}
info->paths.clear();
pthread_mutex_unlock(&(info->mutex));
return result;
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_skyframe_MacOSXFsEventsDiffAwareness_doClose(
JNIEnv *env, jobject fsEventsDiffAwareness) {
JNIEventsDiffAwareness *info = GetInfo(env, fsEventsDiffAwareness);
CFRunLoopStop(info->runLoop);
FSEventStreamStop(info->stream);
FSEventStreamUnscheduleFromRunLoop(info->stream, info->runLoop,
kCFRunLoopDefaultMode);
FSEventStreamInvalidate(info->stream);
FSEventStreamRelease(info->stream);
delete info;
}