wdm 0.0.2-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +22 -0
- data/README.md +58 -0
- data/ext/wdm/entry.c +67 -0
- data/ext/wdm/entry.h +47 -0
- data/ext/wdm/extconf.rb +9 -0
- data/ext/wdm/memory.c +27 -0
- data/ext/wdm/memory.h +32 -0
- data/ext/wdm/monitor.c +70 -0
- data/ext/wdm/monitor.h +50 -0
- data/ext/wdm/queue.c +108 -0
- data/ext/wdm/queue.h +50 -0
- data/ext/wdm/rb_change.c +185 -0
- data/ext/wdm/rb_change.h +28 -0
- data/ext/wdm/rb_monitor.c +531 -0
- data/ext/wdm/rb_monitor.h +39 -0
- data/ext/wdm/utils.c +60 -0
- data/ext/wdm/utils.h +25 -0
- data/ext/wdm/wdm.c +45 -0
- data/ext/wdm/wdm.h +66 -0
- data/ext/wdm/wdm.sln +20 -0
- data/ext/wdm/wdm.vcxproj +104 -0
- data/ext/wdm/wdm.vcxproj.filters +74 -0
- metadata +167 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Maher Sallam
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Windows Directory Monitor (WDM)
|
2
|
+
|
3
|
+
Windows Directory Monitor (WDM) is a library which can be used to monitor directories for changes.
|
4
|
+
It's mostly implemented in C and uses the Win32 API for a better performance.
|
5
|
+
|
6
|
+
**Note:** This is still a work in progress, so it's not advisable to use
|
7
|
+
it yet in anything (unless you are testing it, which is very much appreciated :)).
|
8
|
+
|
9
|
+
TODO:
|
10
|
+
|
11
|
+
- Fix all the TODO's in the source.
|
12
|
+
- ~~Enable watching subdirectories.~~
|
13
|
+
- ~~Add options to the `watch` method.~~
|
14
|
+
- ~~Provide info about the change in the callback.~~
|
15
|
+
- ~~Convert \ to / in paths.~~
|
16
|
+
- ~~Don't allow directories to be watched while the monitor is running.~~
|
17
|
+
- ~~Check if the passed direcoty exists.~~
|
18
|
+
- ~~Convert passed directories to absolute paths.~~
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'wdm'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install wdm
|
33
|
+
|
34
|
+
## Compiling the extension for developers
|
35
|
+
|
36
|
+
Download the source, then run the following:
|
37
|
+
|
38
|
+
$ rake compile
|
39
|
+
|
40
|
+
To get debug messages, you need to enable them in the `global.h` file:
|
41
|
+
|
42
|
+
#define WDM_DEBUG_ENABLED TRUE // This is disabled by default
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
TODO: Write usage instructions here
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
55
|
+
|
56
|
+
## Author
|
57
|
+
|
58
|
+
[Maher Sallam](https://github.com/Maher4Ever)
|
data/ext/wdm/entry.c
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#include "wdm.h"
|
2
|
+
|
3
|
+
#include "memory.h"
|
4
|
+
#include "entry.h"
|
5
|
+
|
6
|
+
// ---------------------------------------------------------
|
7
|
+
// Entry user data functions
|
8
|
+
// ---------------------------------------------------------
|
9
|
+
|
10
|
+
WDM_PEntryUserData
|
11
|
+
wdm_entry_user_data_new() {
|
12
|
+
WDM_PEntryUserData user_data;
|
13
|
+
|
14
|
+
user_data = WDM_ALLOC(WDM_EntryUserData);
|
15
|
+
|
16
|
+
user_data->dir = NULL;
|
17
|
+
user_data->watch_childeren = FALSE;
|
18
|
+
|
19
|
+
return user_data;
|
20
|
+
}
|
21
|
+
|
22
|
+
void
|
23
|
+
wdm_entry_user_data_free(WDM_PEntryUserData user_data) {
|
24
|
+
if ( user_data->dir != NULL ) free(user_data->dir);
|
25
|
+
free(user_data);
|
26
|
+
}
|
27
|
+
|
28
|
+
// ---------------------------------------------------------
|
29
|
+
// Entry functions
|
30
|
+
// ---------------------------------------------------------
|
31
|
+
|
32
|
+
WDM_PEntry
|
33
|
+
wdm_entry_new() {
|
34
|
+
WDM_PEntry entry;
|
35
|
+
|
36
|
+
entry = WDM_ALLOC(WDM_Entry);
|
37
|
+
|
38
|
+
entry->user_data = wdm_entry_user_data_new();
|
39
|
+
entry->dir_handle = INVALID_HANDLE_VALUE;
|
40
|
+
entry->next = NULL;
|
41
|
+
|
42
|
+
ZeroMemory(&entry->buffer, WDM_BUFFER_SIZE);
|
43
|
+
ZeroMemory(&entry->event_container, sizeof(OVERLAPPED));
|
44
|
+
|
45
|
+
return entry;
|
46
|
+
}
|
47
|
+
|
48
|
+
void
|
49
|
+
wdm_entry_free(WDM_PEntry entry) {
|
50
|
+
if ( entry->dir_handle != INVALID_HANDLE_VALUE ) {
|
51
|
+
CancelIo(entry->dir_handle); // Stop monitoring changes
|
52
|
+
CloseHandle(entry->dir_handle);
|
53
|
+
}
|
54
|
+
wdm_entry_user_data_free(entry->user_data);
|
55
|
+
free(entry);
|
56
|
+
}
|
57
|
+
|
58
|
+
void
|
59
|
+
wdm_entry_list_free(WDM_PEntry entry) {
|
60
|
+
WDM_PEntry tmp;
|
61
|
+
|
62
|
+
while(entry != NULL) {
|
63
|
+
tmp = entry;
|
64
|
+
entry = entry->next;
|
65
|
+
wdm_entry_free(tmp);
|
66
|
+
}
|
67
|
+
}
|
data/ext/wdm/entry.h
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#include <Windows.h>
|
2
|
+
#include <ruby.h>
|
3
|
+
|
4
|
+
#ifndef WDM_ENTRY_H
|
5
|
+
#define WDM_ENTRY_H
|
6
|
+
|
7
|
+
#ifdef __cplusplus
|
8
|
+
extern "C" {
|
9
|
+
#endif // __cplusplus
|
10
|
+
|
11
|
+
// ---------------------------------------------------------
|
12
|
+
// Types
|
13
|
+
// ---------------------------------------------------------
|
14
|
+
|
15
|
+
typedef struct {
|
16
|
+
LPWSTR dir; // Name of directory to watch
|
17
|
+
VALUE callback; // Proc object to call when there are changes
|
18
|
+
BOOL watch_childeren; // Watch sub-directories
|
19
|
+
DWORD flags; // Flags for the type of changes to report
|
20
|
+
} WDM_EntryUserData, *WDM_PEntryUserData;
|
21
|
+
|
22
|
+
typedef struct WDM_Entry {
|
23
|
+
WDM_PEntryUserData user_data; // User-supplied data
|
24
|
+
HANDLE dir_handle; // IO handle of the directory
|
25
|
+
BYTE buffer[WDM_BUFFER_SIZE]; // Buffer for the results
|
26
|
+
OVERLAPPED event_container; // Async IO event container
|
27
|
+
struct WDM_Entry* next; // Well, this is a linked list, so this is self-explanatory :)
|
28
|
+
} WDM_Entry, *WDM_PEntry;
|
29
|
+
|
30
|
+
// ---------------------------------------------------------
|
31
|
+
// Prototypes
|
32
|
+
// ---------------------------------------------------------
|
33
|
+
|
34
|
+
WDM_PEntryUserData wdm_entry_user_data_new();
|
35
|
+
void wdm_entry_user_data_free(WDM_PEntryUserData);
|
36
|
+
|
37
|
+
WDM_PEntry wdm_entry_new();
|
38
|
+
void wdm_entry_free(WDM_PEntry);
|
39
|
+
void wdm_entry_list_free(WDM_PEntry);
|
40
|
+
|
41
|
+
// ---------------------------------------------------------
|
42
|
+
|
43
|
+
#ifdef __cplusplus
|
44
|
+
}
|
45
|
+
#endif // __cplusplus
|
46
|
+
|
47
|
+
#endif // WDM_ENTRY_H
|
data/ext/wdm/extconf.rb
ADDED
data/ext/wdm/memory.c
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#include "wdm.h"
|
2
|
+
|
3
|
+
#include "memory.h"
|
4
|
+
|
5
|
+
void *
|
6
|
+
wdm_memory_malloc (size_t size)
|
7
|
+
{
|
8
|
+
void *memory = malloc(size);
|
9
|
+
|
10
|
+
if ( memory == NULL ) {
|
11
|
+
rb_fatal("failed to allocate memory");
|
12
|
+
}
|
13
|
+
|
14
|
+
return memory;
|
15
|
+
}
|
16
|
+
|
17
|
+
void *
|
18
|
+
wdm_memory_realloc (void *ptr, size_t size)
|
19
|
+
{
|
20
|
+
void *memory = realloc(ptr, size);
|
21
|
+
|
22
|
+
if ( memory == NULL ) {
|
23
|
+
rb_fatal("failed to re-allocate memory");
|
24
|
+
}
|
25
|
+
|
26
|
+
return memory;
|
27
|
+
}
|
data/ext/wdm/memory.h
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#ifndef WDM_MEMORY_H
|
2
|
+
#define WDM_MEMORY_H
|
3
|
+
|
4
|
+
#ifdef __cplusplus
|
5
|
+
extern "C" {
|
6
|
+
#endif // __cplusplus
|
7
|
+
|
8
|
+
// ---------------------------------------------------------
|
9
|
+
// Prototypes
|
10
|
+
// ---------------------------------------------------------
|
11
|
+
|
12
|
+
void *
|
13
|
+
wdm_memory_malloc (size_t);
|
14
|
+
|
15
|
+
void *
|
16
|
+
wdm_memory_realloc (void *, size_t);
|
17
|
+
|
18
|
+
// ---------------------------------------------------------
|
19
|
+
// Macros
|
20
|
+
// ---------------------------------------------------------
|
21
|
+
|
22
|
+
#define WDM_ALLOC_N(type,n) ((type*)wdm_memory_malloc((n) * sizeof(type)))
|
23
|
+
#define WDM_ALLOC(type) ((type*)wdm_memory_malloc(sizeof(type)))
|
24
|
+
#define WDM_REALLOC_N(var,type,n) ((var)=(type*)wdm_memory_realloc((void*)(var), (n) * sizeof(type)))
|
25
|
+
|
26
|
+
// ---------------------------------------------------------
|
27
|
+
|
28
|
+
#ifdef __cplusplus
|
29
|
+
}
|
30
|
+
#endif // __cplusplus
|
31
|
+
|
32
|
+
#endif // WDM_MEMORY_H
|
data/ext/wdm/monitor.c
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#include "wdm.h"
|
2
|
+
|
3
|
+
#include "memory.h"
|
4
|
+
#include "entry.h"
|
5
|
+
#include "queue.h"
|
6
|
+
|
7
|
+
#include "monitor.h"
|
8
|
+
|
9
|
+
WDM_PMonitor
|
10
|
+
wdm_monitor_new() {
|
11
|
+
WDM_PMonitor monitor;
|
12
|
+
|
13
|
+
monitor = WDM_ALLOC(WDM_Monitor);
|
14
|
+
|
15
|
+
monitor->running = FALSE;
|
16
|
+
|
17
|
+
monitor->head = NULL;
|
18
|
+
monitor->monitoring_thread = INVALID_HANDLE_VALUE;
|
19
|
+
|
20
|
+
monitor->changes = wdm_queue_new();
|
21
|
+
|
22
|
+
monitor->process_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
23
|
+
monitor->stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
24
|
+
|
25
|
+
if ( ! InitializeCriticalSectionAndSpinCount(&monitor->lock,
|
26
|
+
0x00000400) ) // TODO: look into the best value for spinning.
|
27
|
+
{
|
28
|
+
rb_raise(eWDM_Error, "Can't create a lock for the monitor");
|
29
|
+
}
|
30
|
+
|
31
|
+
return monitor;
|
32
|
+
}
|
33
|
+
|
34
|
+
void
|
35
|
+
wdm_monitor_free(WDM_PMonitor monitor) {
|
36
|
+
if ( monitor->monitoring_thread != INVALID_HANDLE_VALUE ) CloseHandle(monitor->monitoring_thread);
|
37
|
+
|
38
|
+
wdm_entry_list_free(monitor->head);
|
39
|
+
wdm_queue_free(monitor->changes);
|
40
|
+
DeleteCriticalSection(&monitor->lock);
|
41
|
+
CloseHandle(monitor->process_event); // TODO: Look into why this crashes the app when exiting!
|
42
|
+
CloseHandle(monitor->stop_event);
|
43
|
+
|
44
|
+
free(monitor);
|
45
|
+
}
|
46
|
+
|
47
|
+
void
|
48
|
+
wdm_monitor_update_head(WDM_PMonitor monitor, WDM_PEntry new_head) {
|
49
|
+
EnterCriticalSection(&monitor->lock);
|
50
|
+
new_head->next = monitor->head;
|
51
|
+
monitor->head = new_head;
|
52
|
+
LeaveCriticalSection(&monitor->lock);
|
53
|
+
}
|
54
|
+
|
55
|
+
WDM_PMonitorCallbackParam
|
56
|
+
wdm_monitor_callback_param_new(WDM_PMonitor monitor, WDM_PEntry entry) {
|
57
|
+
WDM_PMonitorCallbackParam param;
|
58
|
+
|
59
|
+
param = WDM_ALLOC(WDM_MonitorCallbackParam);
|
60
|
+
|
61
|
+
param->monitor = monitor;
|
62
|
+
param->entry = entry;
|
63
|
+
|
64
|
+
return param;
|
65
|
+
}
|
66
|
+
|
67
|
+
void
|
68
|
+
wdm_monitor_callback_param_free(WDM_PMonitorCallbackParam param) {
|
69
|
+
free(param);
|
70
|
+
}
|
data/ext/wdm/monitor.h
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#include <Windows.h>
|
2
|
+
|
3
|
+
#include "entry.h"
|
4
|
+
#include "queue.h"
|
5
|
+
|
6
|
+
#ifndef WDM_MONITOR_H
|
7
|
+
#define WDM_MONITOR_H
|
8
|
+
|
9
|
+
#ifdef __cplusplus
|
10
|
+
extern "C" {
|
11
|
+
#endif // __cplusplus
|
12
|
+
|
13
|
+
// ---------------------------------------------------------
|
14
|
+
// Types
|
15
|
+
// ---------------------------------------------------------
|
16
|
+
|
17
|
+
typedef struct {
|
18
|
+
BOOL running;
|
19
|
+
WDM_PEntry head;
|
20
|
+
WDM_PQueue changes;
|
21
|
+
CRITICAL_SECTION lock;
|
22
|
+
HANDLE monitoring_thread;
|
23
|
+
HANDLE process_event;
|
24
|
+
HANDLE stop_event;
|
25
|
+
} WDM_Monitor, *WDM_PMonitor;
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
WDM_PEntry entry;
|
29
|
+
WDM_PMonitor monitor;
|
30
|
+
} WDM_MonitorCallbackParam, *WDM_PMonitorCallbackParam;
|
31
|
+
|
32
|
+
// ---------------------------------------------------------
|
33
|
+
// Prototypes
|
34
|
+
// ---------------------------------------------------------
|
35
|
+
|
36
|
+
WDM_PMonitor wdm_monitor_new();
|
37
|
+
void wdm_monitor_free(WDM_PMonitor);
|
38
|
+
|
39
|
+
void wdm_monitor_update_head(WDM_PMonitor, WDM_PEntry);
|
40
|
+
|
41
|
+
WDM_PMonitorCallbackParam wdm_monitor_callback_param_new(WDM_PMonitor, WDM_PEntry);
|
42
|
+
void wdm_monitor_callback_param_free(WDM_PMonitorCallbackParam);
|
43
|
+
|
44
|
+
// ---------------------------------------------------------
|
45
|
+
|
46
|
+
#ifdef __cplusplus
|
47
|
+
}
|
48
|
+
#endif // __cplusplus
|
49
|
+
|
50
|
+
#endif // WDM_MONITOR_H
|
data/ext/wdm/queue.c
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#include "wdm.h"
|
2
|
+
|
3
|
+
#include "memory.h"
|
4
|
+
#include "queue.h"
|
5
|
+
|
6
|
+
// ---------------------------------------------------------
|
7
|
+
// Queue item functions
|
8
|
+
// ---------------------------------------------------------
|
9
|
+
|
10
|
+
WDM_PQueueItem
|
11
|
+
wdm_queue_item_new() {
|
12
|
+
WDM_PQueueItem item;
|
13
|
+
|
14
|
+
item = WDM_ALLOC(WDM_QueueItem);
|
15
|
+
item->user_data = NULL;
|
16
|
+
item->previous = NULL;
|
17
|
+
item->next = NULL;
|
18
|
+
|
19
|
+
ZeroMemory(&item->buffer, WDM_BUFFER_SIZE);
|
20
|
+
|
21
|
+
return item;
|
22
|
+
}
|
23
|
+
|
24
|
+
void
|
25
|
+
wdm_queue_item_free(WDM_PQueueItem item) {
|
26
|
+
// We can't really do anything to the prev pointer nor the next pointer,
|
27
|
+
// because we might break any linking the user has established.
|
28
|
+
free(item);
|
29
|
+
}
|
30
|
+
|
31
|
+
// ---------------------------------------------------------
|
32
|
+
// Queue functions
|
33
|
+
// ---------------------------------------------------------
|
34
|
+
|
35
|
+
WDM_PQueue
|
36
|
+
wdm_queue_new() {
|
37
|
+
WDM_PQueue queue;
|
38
|
+
|
39
|
+
queue = WDM_ALLOC(WDM_Queue);
|
40
|
+
queue->front = NULL;
|
41
|
+
queue->rear = NULL;
|
42
|
+
|
43
|
+
if ( ! InitializeCriticalSectionAndSpinCount(&queue->lock,
|
44
|
+
0x00000400) ) // TODO: look into the best value for spinning.
|
45
|
+
{
|
46
|
+
rb_raise(eWDM_Error, "Can't create a lock for the queue");
|
47
|
+
}
|
48
|
+
|
49
|
+
return queue;
|
50
|
+
}
|
51
|
+
|
52
|
+
void
|
53
|
+
wdm_queue_free(WDM_PQueue queue) {
|
54
|
+
wdm_queue_empty(queue);
|
55
|
+
free(queue);
|
56
|
+
}
|
57
|
+
|
58
|
+
void
|
59
|
+
wdm_queue_enqueue(WDM_PQueue queue, WDM_PQueueItem item) {
|
60
|
+
EnterCriticalSection(&queue->lock);
|
61
|
+
|
62
|
+
if ( queue->rear == NULL && queue->front == NULL ) {
|
63
|
+
queue->front = queue->rear = item;
|
64
|
+
}
|
65
|
+
else {
|
66
|
+
queue->rear->next = item;
|
67
|
+
item->previous = queue->rear;
|
68
|
+
queue->rear = item;
|
69
|
+
}
|
70
|
+
|
71
|
+
LeaveCriticalSection(&queue->lock);
|
72
|
+
}
|
73
|
+
|
74
|
+
WDM_PQueueItem
|
75
|
+
wdm_queue_dequeue(WDM_PQueue queue) {
|
76
|
+
WDM_PQueueItem item;
|
77
|
+
|
78
|
+
EnterCriticalSection(&queue->lock);
|
79
|
+
|
80
|
+
if ( queue->rear == NULL && queue->front == NULL ) {
|
81
|
+
item = NULL;
|
82
|
+
}
|
83
|
+
else {
|
84
|
+
item = queue->front;
|
85
|
+
queue->front = queue->front->next;
|
86
|
+
|
87
|
+
// Reset the rear when the queue is empty
|
88
|
+
if ( queue->front == NULL ) queue->rear = NULL;
|
89
|
+
|
90
|
+
// Don't allow the user to mess with the queue
|
91
|
+
item->previous = item->next = NULL;
|
92
|
+
}
|
93
|
+
|
94
|
+
LeaveCriticalSection(&queue->lock);
|
95
|
+
|
96
|
+
return item;
|
97
|
+
}
|
98
|
+
|
99
|
+
void wdm_queue_empty(WDM_PQueue queue) {
|
100
|
+
while( ! wdm_queue_is_empty(queue) ) {
|
101
|
+
wdm_queue_item_free(wdm_queue_dequeue(queue));
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
BOOL
|
106
|
+
wdm_queue_is_empty(WDM_PQueue queue) {
|
107
|
+
return queue->front == NULL && queue->rear == NULL;
|
108
|
+
}
|