1+ # ----------------------------------------------------------------------
2+ # type
3+ struct SimpleLockFile
4+ path:: String
5+ end
6+
7+ SimpleLockFile () = SimpleLockFile (tempname ())
8+
9+ lock_path (slf:: SimpleLockFile ) = slf. path
10+
11+ # ----------------------------------------------------------------------
12+ # Base
13+ import Base. isfile
14+ isfile (slf:: SimpleLockFile ) = isfile (lock_path (slf))
15+
16+ import Base. rm
17+ rm (slf:: SimpleLockFile ; kwargs... ) = rm (lock_path (slf); kwargs... )
18+
19+ # ----------------------------------------------------------------------
20+ # id
21+ const _lock_path_SEP = " ,"
22+
23+ _validate_id (lock_id:: String ) =
24+ contains (lock_id, _lock_path_SEP) &&
25+ error (" Separator '" , _lock_path_SEP, " ' found in the lock id" )
26+
27+ const _LOCK_ID_DICT = [' a' :' z' ; ' A' :' Z' ; ' 0' :' 9' ]
28+ rand_lkid (n = 10 ) = join (rand (_LOCK_ID_DICT, n))
29+
30+ # ----------------------------------------------------------------------
31+ # write
32+
33+ const _LOCK_DFT_TIME_OUT = 0.0
34+ const _LOCK_DFT_WAIT_TIME = 1.0
35+ const _LOCK_DFT_VALID_TIME = 30.0
36+
37+ function _write_lock_file (lf:: String ;
38+ lkid:: String = rand_lkid (),
39+ vtime:: Float64 = _LOCK_DFT_VALID_TIME
40+ )
41+ ttag = time () + vtime
42+ write (lf, string (lkid, _lock_path_SEP, ttag))
43+ return (lkid, ttag)
44+ end
45+
46+ write_lock_file (slf:: SimpleLockFile ; kwargs... ) =
47+ _write_lock_file (lock_path (slf); kwargs... )
48+
49+ # ----------------------------------------------------------------------
50+ # read
51+
52+ function _read_lock_file (lf:: String )
53+ ! isfile (lf) && return (" " , - 1.0 )
54+ txt = read (lf, String)
55+ spt = split (txt, _lock_path_SEP)
56+ length (spt) != 2 && return (" " , - 1.0 )
57+ lkid = spt[1 ]
58+ ttag = tryparse (Float64, spt[2 ])
59+ ttag = isnothing (ttag) ? - 1.0 : ttag
60+ return (lkid, ttag)
61+ end
62+ read_lock_file (slf:: SimpleLockFile ) = _read_lock_file (lock_path (slf))
63+
64+ # ----------------------------------------------------------------------
65+ # has lock
66+
67+ _is_valid_ttag (ttag) = ttag > time ()
68+
69+ function has_lock (lf:: String , lkid:: String )
70+
71+ # read
72+ curr_lid, ttag = _read_lock_file (lf)
73+
74+ # del if invalid
75+ if ! _is_valid_ttag (ttag)
76+ rm (lf; force = true )
77+ return false
78+ end
79+
80+ # test
81+ return lkid == curr_lid
82+ end
83+
84+ has_lock (slf:: SimpleLockFile , lkid:: String ) = has_lock (lock_path (slf), lkid)
85+
86+ # ----------------------------------------------------------------------
87+ # release
88+
89+ function release_lock (lf:: String , lkid:: String )
90+ ! isfile (lf) && return false
91+ ! has_lock (lf, lkid) && return false
92+ isfile (lf) && rm (lf; force = true )
93+ return true
94+ end
95+
96+ release_lock (slf:: SimpleLockFile , lkid:: String ) = release_lock (lock_path (slf), lkid)
97+
98+ # ----------------------------------------------------------------------
99+ # acquire
100+
101+ function _acquire (lf:: String , lkid:: String = rand_lkid ();
102+ vtime = _LOCK_DFT_VALID_TIME
103+ )
104+ if isfile (lf)
105+ curr_lid, ttag = _read_lock_file (lf)
106+
107+ # check if is taken
108+ if _is_valid_ttag (ttag)
109+
110+ if curr_lid == lkid
111+ return (curr_lid, ttag) # is mine
112+ else
113+ return (" " , ttag) # is taken
114+ end
115+ else
116+ # del if invalid
117+ rm (lf; force = true )
118+ end
119+ end
120+ return _write_lock_file (lf; lkid, vtime)
121+ end
122+
123+ function acquire (lf:: String , lkid:: String = rand_lkid ();
124+ vtime = _LOCK_DFT_VALID_TIME,
125+ wt = _LOCK_DFT_WAIT_TIME,
126+ tout = _LOCK_DFT_TIME_OUT,
127+ force = false
128+ )
129+ if tout > 0.0
130+ t0 = time ()
131+ while true
132+ lkid0, ttag = _acquire (lf, lkid; vtime)
133+ ! isempty (lkid0) && return (lkid, ttag)
134+ if (time () - t0) > tout
135+ if force
136+ rm (lf; force)
137+ return _acquire (lf, lkid; vtime)
138+ else
139+ return (" " , ttag)
140+ end
141+ end
142+ sleep (wt)
143+ end
144+ else
145+ force && rm (lf; force)
146+ return _acquire (lf, lkid; vtime)
147+ end
148+ end
149+
150+ acquire (slf:: SimpleLockFile , lkid:: String = rand_lkid (); kwargs... ) = acquire (lock_path (slf), lkid)
151+
152+ # ----------------------------------------------------------------------
153+ # Base.lock
154+
155+ import Base. lock
156+ """
157+ lock(f::Function, slf::SimpleLockFile, lkid::String = rand_lkid();
158+ vtime = $(_LOCK_DFT_VALID_TIME) ,
159+ wt = $(_LOCK_DFT_WAIT_TIME) ,
160+ tout = $(_LOCK_DFT_TIME_OUT) ,
161+ force = false
162+ )
163+
164+ Acquire the lock, execute `f()` with the lock held, and release the lock when f returns.
165+ If the lock is already locked by a different `lkid`, wait (till `tout`) for it to become available.
166+ During waiting, it will sleep `wt` seconds before re-attemping to acquire.
167+ If `force = true` it will acquire the lock after `tout`.
168+ This method is not fully secure to race, but it must be ok for sllow applications.
169+ Returns `true` if the lock
170+
171+ """
172+ function lock (
173+ f:: Function , slf:: SimpleLockFile , lkid:: String = rand_lkid ();
174+ vtime = _LOCK_DFT_VALID_TIME,
175+ wt = _LOCK_DFT_WAIT_TIME,
176+ tout = _LOCK_DFT_TIME_OUT,
177+ force = false
178+ )
179+
180+ lf = lock_path (slf)
181+ try
182+ acquire (lf, lkid; force, vtime, wt, tout)
183+ f ()
184+ finally
185+ ok_flag = has_lock (lf, lkid)
186+ release_lock (lf, lkid)
187+ return ok_flag
188+ end
189+
190+ end
0 commit comments