Created
March 1, 2010 09:51
-
-
Save andreyvit/318239 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
#******************************************************************************* | |
# Copyright (c) 2000, 2007 IBM Corporation and others. | |
# All rights reserved. This program and the accompanying materials | |
# are made available under the terms of the Eclipse Public License v1.0 | |
# which accompanies this distribution, and is available at | |
# http://www.eclipse.org/legal/epl-v10.html | |
# | |
# Contributors: | |
# IBM Corporation - initial API and implementation | |
#******************************************************************************* | |
cd `dirname $0` | |
OUTPUT_DIR=../../../org.eclipse.swt.cocoa.macosx | |
export OUTPUT_DIR | |
export MACOSX_DEPLOYMENT_TARGET=10.4 | |
make -f make_macosx.mak $1 $2 $3 $4 $5 $6 $7 $8 $9 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.yoursway.fsmonitor; | |
import static com.yoursway.utils.YsCollections.addIfNotNull; | |
import static com.yoursway.utils.YsPathUtils.isChildOrParent; | |
import java.io.File; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashSet; | |
import com.yoursway.fsmonitor.spi.ChangesDetector; | |
import com.yoursway.fsmonitor.spi.ChangesListener; | |
import com.yoursway.fsmonitor.spi.MonitoringRequest; | |
import com.yoursway.utils.YsPathUtils; | |
import com.yoursway.utils.annotations.SynchronizedWithMonitorOfField; | |
import com.yoursway.utils.annotations.UseFromCarbonRunLoopThread; | |
import com.yoursway.utils.annotations.UsedFromJNI; | |
public class ChangesDetectorImpl implements ChangesDetector { | |
static { | |
System.loadLibrary("ys-fs-monitor-macosx_leopard"); | |
} | |
private native void initializeNatives(); | |
private native boolean queueSafeReschedulingRequest(); | |
private native long FSEventStreamCreate(String[] paths, long sinceWhen, double latency); | |
private native void FSEventStreamScheduleWithRunLoop(long streamId); | |
private native boolean FSEventStreamStart(long streamId); | |
private native void FSEventStreamStop(long streamId); | |
private native void FSEventStreamInvalidate(long streamId); | |
private native void FSEventStreamRelease(long streamId); | |
private native static void CFRunLoopRun(); | |
class Request implements MonitoringRequest { | |
private final String monitoredPath; | |
private final ChangesListener listener; | |
public Request(File directory, ChangesListener listener) { | |
if (directory == null) | |
throw new NullPointerException("directory is null"); | |
if (listener == null) | |
throw new NullPointerException("listener is null"); | |
try { | |
directory = directory.getCanonicalFile(); | |
} catch (IOException e) { | |
directory = directory.getAbsoluteFile(); | |
} | |
String directoryPath = directory.getPath(); | |
directoryPath = YsPathUtils.removeTrailingSeparator(directoryPath); | |
this.monitoredPath = directoryPath; | |
this.listener = listener; | |
} | |
public String path() { | |
return monitoredPath; | |
} | |
public void addNotifiersTo(Collection<Runnable> notifiers, String[] paths) { | |
String monitoredPath = this.monitoredPath; | |
for (final String path : paths) | |
if (isChildOrParent(monitoredPath, path)) | |
notifiers.add(new Runnable() { | |
public void run() { | |
listener.pathChanged(path); | |
} | |
}); | |
} | |
public void dispose() { | |
Collection<String> paths; | |
long changeId; | |
synchronized (activeRequests) { | |
activeRequests.remove(this); | |
paths = calculateActivePaths(); | |
changeId = activeRequestsChangeCount++; | |
} | |
schedule(paths, changeId); | |
} | |
} | |
@UseFromCarbonRunLoopThread | |
private long lastSeenEventId = -1; | |
@SuppressWarnings("unused") | |
@UsedFromJNI | |
private long runLoopHandle = 0; | |
/** | |
* A collection of currently active (non-disposed) monitoring requests. | |
*/ | |
@SynchronizedWithMonitorOfField("activeRequests") | |
private Collection<Request> activeRequests = new HashSet<Request>(); | |
@SynchronizedWithMonitorOfField("activeRequests") | |
private long activeRequestsChangeCount = 0; | |
/** | |
* In <code>ACTIVE</code> state, the paths that are currently being | |
* monitored by FSEventStream. | |
* | |
* In <code>RESCHEDULING</code> state, the paths that will be monitored by | |
* FSEventStream once the rescheduling is complete. | |
*/ | |
@SynchronizedWithMonitorOfField("stateLock") | |
private Collection<String> currentlyMonitoredPaths = new HashSet<String>(); | |
/** | |
* Used to prevent later changes from being lost due to a race condition | |
* that might occur because we are using two separate locks. | |
*/ | |
@SynchronizedWithMonitorOfField("stateLock") | |
private long lastScheduledChangedId = -1; | |
@SynchronizedWithMonitorOfField("stateLock") | |
private long streamId; | |
@SynchronizedWithMonitorOfField("stateLock") | |
private State state = State.INACTIVE; | |
@SynchronizedWithMonitorOfField("stateLock") | |
private boolean isScheduled = false; | |
private Object stateLock = new Object(); | |
public ChangesDetectorImpl() { | |
initializeNatives(); | |
} | |
public MonitoringRequest monitor(File directory, ChangesListener listener) { | |
Request newRequest = new Request(directory, listener); | |
Collection<String> paths; | |
long changeId; | |
synchronized (activeRequests) { | |
activeRequests.add(newRequest); | |
paths = calculateActivePaths(); | |
changeId = activeRequestsChangeCount++; | |
} | |
schedule(paths, changeId); | |
return newRequest; | |
} | |
private Collection<String> calculateActivePaths() { | |
Collection<String> paths; | |
paths = new HashSet<String>(activeRequests.size()); | |
for (Request request : activeRequests) | |
addIfNotNull(paths, request.path()); | |
return paths; | |
} | |
void schedule(Collection<String> newPaths, long changeId) { | |
synchronized (stateLock) { | |
if (!state.canChangeToAnotherState()) | |
return; | |
if (changeId < lastScheduledChangedId) | |
return; // prevent later changes from being lost | |
lastScheduledChangedId = changeId; | |
if (newPaths.equals(currentlyMonitoredPaths)) | |
return; | |
currentlyMonitoredPaths = newPaths; | |
if (!state.shouldInitiateRescheduling()) | |
return; | |
state = State.RESCHEDULING; | |
} | |
scheduleRestart(); | |
} | |
private void scheduleRestart() { | |
if (!queueSafeReschedulingRequest()) | |
handleSafeToReschedule(); | |
} | |
@UsedFromJNI | |
void handleChange(String[] paths, long[] eventIds) { | |
// System.out.println("CHANGE!"); | |
// for (int i = 0; i < eventIds.length; i++) { | |
// String path = paths[i]; | |
// long id = eventIds[i]; | |
// if (id > lastSeenEventId) | |
// lastSeenEventId = id; | |
// System.out.println(" #" + id + " - " + path); | |
// } | |
// System.out.flush(); | |
for (int i = 0; i < paths.length; i++) | |
paths[i] = removeTrailingSeparator(paths[i]); | |
// minimize the time we spend inside the synchronized area | |
Collection<Runnable> notifiers = new ArrayList<Runnable>(); | |
synchronized (activeRequests) { | |
for (Request request : activeRequests) | |
request.addNotifiersTo(notifiers, paths); | |
} | |
for (Runnable runnable : notifiers) | |
runnable.run(); | |
} | |
@UsedFromJNI | |
void handleSafeToReschedule() { | |
synchronized (state) { | |
if (isScheduled) { | |
FSEventStreamStop(streamId); | |
// System.out.println("FSEventStream stopped."); | |
FSEventStreamInvalidate(streamId); | |
// System.out.println("FSEventStream invalidated."); | |
FSEventStreamRelease(streamId); | |
System.out.println("FSEventStream disposed."); | |
isScheduled = false; | |
} | |
if (currentlyMonitoredPaths.isEmpty() || !state.canActivate()) { | |
if (state.canChangeToAnotherState()) | |
state = State.INACTIVE; | |
} else { | |
String[] paths = currentlyMonitoredPaths.toArray(new String[currentlyMonitoredPaths.size()]); | |
streamId = FSEventStreamCreate(paths, lastSeenEventId, 1.0); | |
if (streamId == 0) | |
throw new RuntimeException("Cannot create FSEventStream"); | |
// System.out.println("FSEventStream created."); | |
FSEventStreamScheduleWithRunLoop(streamId); | |
// System.out.println("FSEventStream scheduled."); | |
if (!FSEventStreamStart(streamId)) | |
throw new RuntimeException("Cannot start FSEventStream"); | |
System.out.println("FSEventStream running..."); | |
isScheduled = true; | |
if (state.canChangeToAnotherState()) | |
state = State.ACTIVE; | |
} | |
} | |
} | |
public static void main(String[] args) { | |
ChangesDetectorImpl detector = new ChangesDetectorImpl(); | |
detector.monitor(new File("/Users/andreyvit"), new ChangesListener() { | |
public void pathChanged(String path) { | |
System.out.println("Changed: " + path); | |
} | |
}); | |
CFRunLoopRun(); | |
detector.dispose(); | |
} | |
public void dispose() { | |
synchronized(state) { | |
state = State.DISPOSED; | |
} | |
scheduleRestart(); | |
} | |
/** | |
* @deprecated Use {@link YsPathUtils#removeTrailingSeparator(String)} instead | |
*/ | |
public static String removeTrailingSeparator(String directoryPath) { | |
return YsPathUtils.removeTrailingSeparator(directoryPath); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#******************************************************************************* | |
# Copyright (c) 2000, 2007 IBM Corporation and others. | |
# All rights reserved. This program and the accompanying materials | |
# are made available under the terms of the Eclipse Public License v1.0 | |
# which accompanies this distribution, and is available at | |
# http://www.eclipse.org/legal/epl-v10.html | |
# | |
# Contributors: | |
# IBM Corporation - initial API and implementation | |
#******************************************************************************* | |
# Makefile for SWT libraries on Cocoa/Mac | |
# include make_common.mak | |
maj_ver=1 | |
min_ver=0 | |
OUTPUT_DIR=../ | |
SWT_PREFIX=ys-fs-monitor | |
WS_PREFIX=macosx_leopard | |
SWT_VERSION=$(maj_ver)$(min_ver) | |
SWT_LIB=lib$(SWT_PREFIX)-$(WS_PREFIX).jnilib | |
# Uncomment for Native Stats tool | |
#NATIVE_STATS = -DNATIVE_STATS | |
#SWT_DEBUG = -g | |
ARCHS = -arch i386 -arch ppc | |
CFLAGS = -c -xobjective-c -Wall $(ARCHS) -DSWT_VERSION=$(SWT_VERSION) $(NATIVE_STATS) $(SWT_DEBUG) -DUSE_ASSEMBLER -DCOCOA \ | |
-I /System/Library/Frameworks/JavaVM.framework/Headers \ | |
-I /System/Library/Frameworks/Cocoa.framework/Headers | |
LFLAGS = -bundle $(ARCHS) -framework JavaVM -framework Cocoa -framework Carbon -framework CoreServices -framework WebKit | |
SWT_OBJECTS = os.o | |
all: $(SWT_LIB) | |
.c.o: | |
cc $(CFLAGS) $*.c | |
$(SWT_LIB): $(SWT_OBJECTS) | |
cc -o $(SWT_LIB) $(LFLAGS) $(SWT_OBJECTS) | |
install: all | |
cp *.jnilib $(OUTPUT_DIR) | |
clean: | |
rm -f *.jnilib *.o |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
* Copyright (c) 2000, 2007 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
#include "os.h" | |
#include "os_stats.h" | |
#define OS_NATIVE(func) Java_com_yoursway_fsmonitor_ChangesDetectorImpl_##func | |
typedef struct { | |
JavaVM *vm; | |
jobject that; | |
jmethodID callback_method_id; | |
} OurContext; | |
JNIEnv *get_env(JavaVM *vm) { | |
JNIEnv *env; | |
(*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4); | |
return env; | |
} | |
void OurContextRelease(void *clientCallBackInfo) { | |
JNIEnv *env = get_env(((OurContext *) clientCallBackInfo)->vm); | |
jobject that = ((OurContext *) clientCallBackInfo)->that; | |
(*env)->DeleteGlobalRef(env, that); | |
free(clientCallBackInfo); | |
} | |
void OurFSEventStreamCallback(ConstFSEventStreamRef streamRef, | |
void *clientCallBackInfo, | |
size_t numEvents, | |
void *eventPaths, | |
const FSEventStreamEventFlags eventFlags[], | |
const FSEventStreamEventId eventIds[]) | |
{ | |
JNIEnv *env = get_env(((OurContext *) clientCallBackInfo)->vm); | |
jobject that = ((OurContext *) clientCallBackInfo)->that; | |
jmethodID callback_method_id = ((OurContext *) clientCallBackInfo)->callback_method_id; | |
jclass string_class = (*env)->FindClass(env, "java/lang/String"); | |
jobjectArray paths_array = (*env)->NewObjectArray(env, numEvents, string_class, NULL); | |
jlongArray eventids_array = (*env)->NewLongArray(env, numEvents); | |
int i; | |
for (i = 0; i < numEvents; i++) { | |
(*env)->SetLongArrayRegion(env, eventids_array, 0, numEvents, (jlong*) eventIds); | |
jstring path = (*env)->NewStringUTF(env, ((const char**) eventPaths)[i]); | |
(*env)->SetObjectArrayElement(env, paths_array, i, path); | |
} | |
(*env)->CallVoidMethod(env, that, callback_method_id, paths_array, eventids_array); | |
if ((*env)->ExceptionOccurred(env)) { | |
fprintf(stderr, "* java exception occurred\n"); | |
(*env)->ExceptionDescribe(env); | |
fflush(stdout); | |
} | |
} | |
JNIEXPORT jlong JNICALL OS_NATIVE(FSEventStreamCreate) | |
(JNIEnv *env, jobject that, jarray paths, jlong since_when, jdouble latency) | |
{ | |
OurContext *our_context = (OurContext *) malloc(sizeof(OurContext)); | |
(*env)->GetJavaVM(env, &our_context->vm); | |
our_context->that = (*env)->NewGlobalRef(env, that); | |
jclass klass = (*env)->GetObjectClass(env, that); | |
our_context->callback_method_id = (*env)->GetMethodID(env, klass, "handleChange", "([Ljava/lang/String;[J)V"); | |
if (NULL == our_context->callback_method_id) { | |
free(our_context); | |
return 0; | |
} | |
int i; | |
FSEventStreamEventId since_when_id = (since_when < 0 ? kFSEventStreamEventIdSinceNow : since_when); | |
jsize path_count = (*env)->GetArrayLength(env, paths); | |
CFStringRef refs_array[path_count]; | |
for (i = 0; i < path_count; i++) { | |
jstring path = (*env)->GetObjectArrayElement(env, paths, i); | |
const char *utf = (*env)->GetStringUTFChars(env, path, NULL); | |
refs_array[i] = CFStringCreateWithCString(kCFAllocatorDefault, utf, kCFStringEncodingUTF8); | |
(*env)->ReleaseStringUTFChars(env, path, utf); | |
} | |
CFArrayRef paths_array = CFArrayCreate(kCFAllocatorDefault, (const void**) &refs_array, path_count, &kCFTypeArrayCallBacks); | |
for (i = 0; i < path_count; i++) | |
CFRelease(refs_array[i]); | |
FSEventStreamContext context = {0}; | |
context.info = our_context; | |
context.release = &OurContextRelease; | |
FSEventStreamRef stream = FSEventStreamCreate(kCFAllocatorDefault, &OurFSEventStreamCallback, | |
&context, paths_array, since_when_id, latency, kFSEventStreamCreateFlagNoDefer); | |
CFRelease(paths_array); | |
return (jlong) stream; | |
} | |
JNIEXPORT void JNICALL OS_NATIVE(FSEventStreamScheduleWithRunLoop) (JNIEnv *env, jobject that, jlong stream_id) { | |
jclass that_class = (*env)->GetObjectClass(env, that); | |
jfieldID loop_field = (*env)->GetFieldID(env, that_class, "runLoopHandle", "J"); | |
CFRunLoopRef loop = (CFRunLoopRef) (*env)->GetLongField(env, that, loop_field); | |
FSEventStreamScheduleWithRunLoop((FSEventStreamRef) stream_id, loop, kCFRunLoopCommonModes); | |
} | |
JNIEXPORT jboolean JNICALL OS_NATIVE(FSEventStreamStart) (JNIEnv *env, jobject that, jlong stream_id) { | |
return FSEventStreamStart((FSEventStreamRef) stream_id) ? true : false; | |
} | |
JNIEXPORT void JNICALL OS_NATIVE(FSEventStreamStop) (JNIEnv *env, jobject that, jlong stream_id) { | |
FSEventStreamStop((FSEventStreamRef) stream_id); | |
} | |
JNIEXPORT void JNICALL OS_NATIVE(FSEventStreamInvalidate) (JNIEnv *env, jobject that, jlong stream_id) { | |
FSEventStreamInvalidate((FSEventStreamRef) stream_id); | |
} | |
JNIEXPORT void JNICALL OS_NATIVE(FSEventStreamRelease) (JNIEnv *env, jobject that, jlong stream_id) { | |
FSEventStreamRelease((FSEventStreamRef) stream_id); | |
} | |
JNIEXPORT void JNICALL OS_NATIVE(CFRunLoopRun) (JNIEnv *env, jclass that) { | |
CFRunLoopRun(); | |
} | |
void OurTimerCallback(CFRunLoopTimerRef timer, void *info) { | |
JNIEnv *env = get_env(((OurContext *) info)->vm); | |
jobject that = ((OurContext *) info)->that; | |
jmethodID callback_method_id = ((OurContext *) info)->callback_method_id; | |
(*env)->CallVoidMethod(env, that, callback_method_id); | |
if ((*env)->ExceptionOccurred(env)) { | |
fprintf(stderr, "* java exception occurred\n"); | |
(*env)->ExceptionDescribe(env); | |
fflush(stdout); | |
} | |
} | |
JNIEXPORT void JNICALL OS_NATIVE(initializeNatives) (JNIEnv *env, jobject that) { | |
CFRunLoopRef loop = CFRunLoopGetCurrent(); | |
jclass that_class = (*env)->GetObjectClass(env, that); | |
jfieldID loop_field = (*env)->GetFieldID(env, that_class, "runLoopHandle", "J"); | |
(*env)->SetLongField(env, that, loop_field, (jlong) loop); | |
} | |
JNIEXPORT jboolean JNICALL OS_NATIVE(queueSafeReschedulingRequest) (JNIEnv *env, jobject that) { | |
OurContext *our_context = (OurContext *) malloc(sizeof(OurContext)); | |
(*env)->GetJavaVM(env, &our_context->vm); | |
our_context->that = (*env)->NewGlobalRef(env, that); | |
jclass klass = (*env)->GetObjectClass(env, that); | |
our_context->callback_method_id = (*env)->GetMethodID(env, klass, "handleSafeToReschedule", "()V"); | |
if (NULL == our_context->callback_method_id) { | |
free(our_context); | |
return 0; | |
} | |
jclass that_class = (*env)->GetObjectClass(env, that); | |
jfieldID loop_field = (*env)->GetFieldID(env, that_class, "runLoopHandle", "J"); | |
CFRunLoopRef loop = (CFRunLoopRef) (*env)->GetLongField(env, that, loop_field); | |
CFRunLoopTimerContext context = {0}; | |
context.info = our_context; | |
context.release = &OurContextRelease; | |
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, &OurTimerCallback, &context); | |
CFRunLoopAddTimer(loop, timer, kCFRunLoopCommonModes); | |
return true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
* Copyright (c) 2000, 2006 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
#ifndef INC_os_H | |
#define INC_os_H | |
/*#define NDEBUG*/ | |
#include "jni.h" | |
/* #include <Cocoa/Cocoa.h> */ | |
#include <CoreServices/CoreServices.h> | |
/* #import <objc/objc-runtime.h> */ | |
#ifndef __i386__ | |
#define objc_msgSend_fpret objc_msgSend | |
#endif | |
#endif /* INC_os_H */ | |
#ifndef NATIVE_STATS | |
#define OS_NATIVE_ENTER(env, that, func) \ | |
@try { | |
#define OS_NATIVE_EXIT(env, that, func) \ | |
} \ | |
@catch (NSException *nsx) { \ | |
jclass threadClass = (*env)->FindClass(env, "java/lang/Thread"); \ | |
jmethodID dumpStackID = (*env)->GetStaticMethodID(env, threadClass, "dumpStack", "()V"); \ | |
if (dumpStackID != NULL) (*env)->CallStaticVoidMethod(env, threadClass, dumpStackID, 0); \ | |
@throw; \ | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment