aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Inotify.dcl4
-rw-r--r--Inotify.icl38
-rw-r--r--Makefile6
-rw-r--r--inotify_c.c16
-rw-r--r--test.icl29
-rw-r--r--test_reload.icl69
-rw-r--r--test_reload.prj59
8 files changed, 196 insertions, 29 deletions
diff --git a/.gitignore b/.gitignore
index 1fc2545..53697c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
# Executables
*.exe
*.out
-test
# Directory used to store object files, abc files and assembly files
Clean System Files/
@@ -9,3 +8,6 @@ Clean System Files/
# iTasks environment extra data
*-data/
sapl/
+
+test
+test_reload
diff --git a/Inotify.dcl b/Inotify.dcl
index e021c45..3811abc 100644
--- a/Inotify.dcl
+++ b/Inotify.dcl
@@ -9,7 +9,7 @@ from Data.Maybe import ::Maybe
:: INMask :== Int
:: INEvent :== Int
-:: INCallback st :== INEvent st *World -> *(st, *World)
+:: INCallback st :== INEvent (Maybe String) st *World -> *(st, *World)
(|-) infixl 6 :: (INMask INMask -> INMask)
@@ -23,6 +23,8 @@ inotify_rm_watch :: !INWatch !*(Inotify st) -> *(Bool, *Inotify st)
inotify_poll :: *(Inotify st) -> *Inotify st
inotify_check :: *(Inotify st) *World -> *(*Inotify st, *World)
+inotify_loop_forever :: *(Inotify st) *World -> *(*Inotify st, *World)
+
IN_ACCESS :== 0x00000001 // File was accessed
IN_MODIFY :== 0x00000002 // File was modified
IN_ATTRIB :== 0x00000004 // Metadata changed
diff --git a/Inotify.icl b/Inotify.icl
index 20ce3e9..e676f03 100644
--- a/Inotify.icl
+++ b/Inotify.icl
@@ -1,6 +1,7 @@
implementation module Inotify
import Data.Either
+import Data.List
import Data.Maybe
import StdArray
@@ -74,22 +75,25 @@ where
inotify_check :: *(Inotify st) *World -> *(*Inotify st, *World)
inotify_check inot=:{fd,watches,state} w
- # (ok, wds, masks, fd) = c_check fd
+ # (ok, wds, masks, fnames, fd) = c_check fd
inot = { inot & fd=fd }
| not ok = (inot, w)
| (size wds) rem 4 <> 0 || (size masks) rem 4 <> 0 = (inot,w)
- # (wds,masks) = (split 4 wds, split 4 masks)
+ # (wds,masks,fnames) = (split 4 wds, split 4 masks, splitOn '\0' fnames)
| length wds <> length masks = (inot, w)
- # wdsmasks = zip2 (map bytesToInt wds) (map bytesToInt masks)
- # (fd,st,w`) = seq (map (check wdsmasks) watches) (inot.fd, state, w)
+ # infos = zip3 (map bytesToInt wds) (map bytesToInt masks) fnames
+ # (fd,st,w`) = seq (map (check infos) watches) (inot.fd, state, w)
= ({ inot & fd=fd, state=st }, w`)
where
- check :: [(Int,Int)] (INWatch, INCallback st) *(*Int, st, *World)
+ check :: [(Int,Int,String)] (INWatch, INCallback st) *(*Int, st, *World)
-> *(*Int, st, *World)
- check wdsmasks (watch,f) (fd,st,w)
- # (st,w) = seq [\(st,w) -> f mask st w
- \\ (wd,mask) <- wdsmasks | wd == watch] (st,w)
+ check infos (watch,f) (fd,st,w)
+ # (st,w) = seq [\(st,w) -> f mask (toMaybe name) st w
+ \\ (wd,mask,name) <- infos | wd == watch] (st,w)
= (fd,st,w)
+ where
+ toMaybe :: String -> Maybe String
+ toMaybe s = if (s=="") Nothing (Just s)
bytesToInt :: {#Char} -> Int
bytesToInt cs = sum [toInt c * (8 ^ p) \\ c <-: cs & p <- [0..]]
@@ -99,12 +103,26 @@ where
| size s > n = [s % (0,n-1) : split n (s % (n, size s - 1))]
| size s == n = [s]
| s == "" = []
+
+ splitOn :: Char String -> [String]
+ splitOn c s = map toString (split` c [c \\ c <-: s])
+ where
+ split` :: Char [Char] -> [[Char]]
+ split` c [] = []
+ split` c cs=:[x:xs]
+ = let (l,r) = span ((<>)c) cs in [l:split` c (removeMember c r)]
- c_check :: !*Int -> *(!Bool, !String, !String, !*Int)
+ c_check :: !*Int -> *(!Bool, !String, !String, !String, !*Int)
c_check fd = code {
- ccall clean_inotify_check "I:VISSI"
+ ccall clean_inotify_check "I:VISSSI"
}
+inotify_loop_forever :: *(Inotify st) *World -> *(*Inotify st, *World)
+inotify_loop_forever inot w
+ # inot = inotify_poll inot
+ # (inot,w) = inotify_check inot w
+ = inotify_loop_forever inot w
+
errno :: Int
errno = err 0
where
diff --git a/Makefile b/Makefile
index 8ff77c4..d24c3a1 100644
--- a/Makefile
+++ b/Makefile
@@ -11,9 +11,15 @@ $(INO_OBJ): inotify_c.c
test: test.icl $(wildcard *.*cl) $(INO_OBJ)
$(CPM) project $@.prj build
+test_reload: test_reload.icl $(wildcard *.*cl) $(INO_OBJ)
+ $(CPM) project $@.prj build
+
run_test: test
./test
+run_test_reload: test_reload
+ while :; do ./test_reload; done
+
clean:
rm -rfv Clean\ System\ Files
diff --git a/inotify_c.c b/inotify_c.c
index f178fa6..c81f57d 100644
--- a/inotify_c.c
+++ b/inotify_c.c
@@ -54,9 +54,11 @@ void clean_poll(int fd, int *re_nrevents, int *re_fd) {
static CleanStringVariable(wds_string, 1024);
static CleanStringVariable(masks_string, 1024);
+static CleanStringVariable(names_string, 4096);
void clean_inotify_check(int fd,
- int *re_ok, CleanString* re_wds, CleanString* re_masks, int *re_fd) {
+ int *re_ok, CleanString* re_wds, CleanString* re_masks,
+ CleanString* re_fnames, int *re_fd) {
char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
const struct inotify_event *ev;
ssize_t len;
@@ -67,17 +69,20 @@ void clean_inotify_check(int fd,
char *wds_ptr = CleanStringCharacters(wds_string);
char *masks_ptr = CleanStringCharacters(masks_string);
+ char *names_ptr = CleanStringCharacters(names_string);
CleanStringLength(wds_string) = 0;
CleanStringLength(masks_string) = 0;
+ CleanStringLength(names_string) = 0;
*re_ok = 0;
*re_fd = fd;
*re_wds = (CleanString) &empty_string;
*re_masks = (CleanString) &empty_string;
+ *re_fnames = (CleanString) &empty_string;
for (;;) {
- poll_n = poll(&pfd, 1, 50);
+ poll_n = poll(&pfd, 1, 0);
if (poll_n < 0) {
return;
} else if (poll_n == 0) {
@@ -103,11 +108,18 @@ void clean_inotify_check(int fd,
memcpy(wds_ptr, &ev->wd, sizeof(int));
wds_ptr += sizeof(int);
CleanStringLength(wds_string) += sizeof(int);
+
+ int len = strlen(ev->name);
+ memcpy(names_ptr, &ev->name, len);
+ names_ptr += len + 1;
+ *(names_ptr - 1) = '\00';
+ CleanStringLength(names_string) += len + 1;
}
}
*re_wds = (CleanString) wds_string;
*re_masks = (CleanString) masks_string;
+ *re_fnames = (CleanString) names_string;
*re_ok = 1;
}
diff --git a/test.icl b/test.icl
index bcb64a9..b598c0f 100644
--- a/test.icl
+++ b/test.icl
@@ -11,21 +11,20 @@ Start w
= inotify_add_watch (echo "file1") IN_ALL_EVENTS "file1" inot
# (Right watch, inot)
= inotify_add_watch (echo "file2") IN_ALL_EVENTS "file2" inot
-# (io,w) = stdio w
-# io = io <<< "Do something with file1 or file2\n"
-# (ok,w) = fclose io w
-# (inot, w) = loop inot w
+# (io,w) = stdio w
+# io = io <<< "Do something with file1 or file2\n"
+# (ok,w) = fclose io w
+# (inot, w) = inotify_loop_forever inot w
= inotify_close inot
where
- loop :: !*(Inotify st) !*World -> *(*Inotify st, *World)
- loop inot w
- # inot = inotify_poll inot
- # (inot, w) = inotify_check inot w
- = loop inot w
-
- echo :: String INEvent Int *World -> *(Int, *World)
- echo fname ev i w
- # (io,w) = stdio w
- # io = io <<< "EVENT: [" <<< fname <<< "; " <<< ev <<< "]\n"
- # (ok,w) = fclose io w
+ echo :: String INEvent (Maybe String) Int *World -> *(Int, *World)
+ echo fname ev f i w
+ # (io,w) = stdio w
+ # io = io <<< "EVENT: ["<<< fname <<<"; "<<< ev <<<"; "<<< f <<<"]\n"
+ # (ok,w) = fclose io w
= (i, w)
+
+instance <<< (Maybe a) | <<< a
+where
+ (<<<) f Nothing = f
+ (<<<) f (Just x) = f <<< x
diff --git a/test_reload.icl b/test_reload.icl
new file mode 100644
index 0000000..052c4c9
--- /dev/null
+++ b/test_reload.icl
@@ -0,0 +1,69 @@
+module test_reload
+
+import StdBool
+import StdClass
+import StdFile
+import StdFunc
+import StdInt
+import StdString
+import Data.Either
+import Data.Maybe
+import Inotify
+
+/** test_reload
+ *
+ * This module will watch the directory it is in. Whenever the attributes of
+ * its own binaries change, it will exit. Hence, putting it in a loop:
+ *
+ * while :; do ./test_reload; done
+ *
+ * will give a program that automatically reloads itself when its binary has
+ * changed.
+ *
+ * Test it by running `make run_test_reload`, then editing `changeme` below and
+ * running `make test_reload` to recompile.
+ */
+
+my_name :== "test_reload"
+my_dir :== "."
+changeme :== 42
+verbose :== False
+
+:: Void = Void
+
+Start w
+# (io,w) = stdio w
+# io = io <<< changeme <<< "\n"
+# (ok,w) = fclose io w
+# (Just inot) = inotify_init Void
+# (Right watch,inot) = inotify_add_watch reload IN_ALL_EVENTS my_dir inot
+# (inot,w) = inotify_loop_forever inot w
+= inotify_close inot
+where
+ reload :: !INEvent (Maybe String) Void !*World -> *(Void, *World)
+ reload ev mbName _ w
+ # w = echo (\f -> f <<< "event: " <<< ev <<< "; " <<< mbName) w
+ | isNothing mbName = (Void, w)
+ # (Just name) = mbName
+ | ev bitand IN_ATTRIB <> 0 && name == my_name
+ # w = echo (\f -> f <<< "reloading...") w
+ # w = exit 127 w
+ = (Void, w)
+ = (Void, w)
+
+ echo :: (*File -> *File) *World -> *World
+ echo f w
+ # (io,w) = stdio w
+ # io = if verbose (flip (<<<) "\n" o f) id io
+ # (ok,w) = fclose io w
+ = w
+
+ exit :: !Int !*World -> *World
+ exit c w = code {
+ ccall exit "I:V:A"
+ }
+
+instance <<< (Maybe x) | <<< x
+where
+ (<<<) f Nothing = f
+ (<<<) f (Just x) = f <<< x
diff --git a/test_reload.prj b/test_reload.prj
new file mode 100644
index 0000000..fa8ddec
--- /dev/null
+++ b/test_reload.prj
@@ -0,0 +1,59 @@
+Version: 1.4
+Global
+ ProjectRoot: .
+ Target: StdEnv
+ Exec: {Project}/test_reload
+ CodeGen
+ CheckStacks: False
+ CheckIndexes: True
+ Application
+ HeapSize: 2097152
+ StackSize: 512000
+ ExtraMemory: 8192
+ IntialHeapSize: 204800
+ HeapSizeMultiplier: 4096
+ ShowExecutionTime: False
+ ShowGC: False
+ ShowStackSize: False
+ MarkingCollector: False
+ DisableRTSFlags: False
+ StandardRuntimeEnv: True
+ Profile
+ Memory: False
+ MemoryMinimumHeapSize: 0
+ Time: False
+ Stack: False
+ Output
+ Output: ShowConstructors
+ Font: Monaco
+ FontSize: 9
+ WriteStdErr: False
+ Link
+ LinkMethod: Static
+ GenerateRelocations: False
+ GenerateSymbolTable: False
+ GenerateLinkMap: False
+ LinkResources: False
+ ResourceSource:
+ GenerateDLL: False
+ ExportedNames:
+ Paths
+ Path: {Project}
+ Path: {Application}/lib/clean-platform/OS-Independent
+ Path: {Application}/lib/Generics
+ Precompile:
+ Postlink:
+MainModule
+ Name: test_reload
+ Dir: {Project}
+ Compiler
+ NeverMemoryProfile: False
+ NeverTimeProfile: False
+ StrictnessAnalysis: True
+ ListTypes: StrictExportTypes
+ ListAttributes: True
+ Warnings: True
+ Verbose: True
+ ReadableABC: False
+ ReuseUniqueNodes: True
+ Fusion: False