-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathsetfilesemaphore.m
More file actions
403 lines (316 loc) · 14.4 KB
/
setfilesemaphore.m
File metadata and controls
403 lines (316 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
function semaphoreCell = setfilesemaphore(fileList, semaphoreMode)
%SETFILESEMAPHORE Set semaphore for exclusive file access.
% SEMAPHORE = SETFILESEMAPHORE(FILENAME) sets a semaphore to get
% exclusive access on file FILENAME. The semaphore is realized by generating
% a simple Matlab data file after checking that no other semaphores are
% existing. The function exits if the semaphore is set. Exclusive file
% access is of course only guaranteed if all other Matlab processes use
% semaphores to access the same file.
%
% The output variable SEMAPHORE is needed to correctly remove the file
% semaphore after file access. It is an error to call function
% SETFILESEMAPHORE without output arguments.
%
% SEMAPHORE = SETFILESEMAPHORE(FILELIST) sets semaphores for all files
% given in cell array FILELIST. Note that function SETFILESEMAPHORE waits
% for exclusive file access on ALL files in the list before returning.
%
% SEMAPHORE = SETFILESEMAPHORE(FILENAME, 'set if existing') sets a
% semaphore only if the file FILENAME is existing. Caution: No exclusive
% file access is guaranteed if the file is not existing! The calling
% function needs to make sure not to access file FILENAME!
%
% SEMAPHORE = SETFILESEMAPHORE(FILENAME, 'set always') will set a semaphore
% file regardless of the existence of file FILENAME (default behaviour).
%
% Note: In order to avoid blocking of other processes, a semaphore file older
% than 20 seconds is considered invalid and will be ignored. Thus the user
% should make sure that the file access time is minimized, i.e. the time
% between setting and removing the file semaphore.
%
% Example:
% sem = setfilesemaphore('test.mat');
% % access file test.mat here
% dir test.mat.semaphore.*
% removefilesemaphore(sem);
%
% Markus Buehren
% Last modified 20.07.2014
%
% See also REMOVEFILESEMAPHORE.
% Todo: What about system time differences between the local machine and
% the machine where the temporary directory lies?
import multicore.*
% set parameters (all times in seconds)
semaphoreOldTime = 20; % other semaphore files are deleted if found longer than this time
semaphoreOldTime2 = 60; % other semaphore files are deleted if older than this time
fixedWaitTime = 0.02; % wait after generating semaphore to check if access is exclusive
checkAgainWaitTime = 0.02; % wait before checking again if semaphore files are already existing
retryWaitTime = 0.4; % random wait time after removing own semaphore file again
waitInfoTime = 10;
removeOldSemaphores = 0; % try to remove old semaphore files or not
% debug options
showWarnings = 0;
debugMode = 0;
if nargout ~= 1
error('Function %s must be called with one output argument!', mfilename);
end
if ~exist('semaphoreMode', 'var')
semaphoreMode = 'set always';
end
setSemaphoreForNotExistingFilesOnly = false;
setSemaphoreForExistingFilesOnly = false;
if ~ischar(semaphoreMode)
error('Error: Mode must be a string.\n');
elseif strcmpi(semaphoreMode, 'set if existing')
setSemaphoreForExistingFilesOnly = true;
%elseif strcmpi(semaphoreMode, 'set if not existing')
% setSemaphoreForNotExistingFilesOnly = true;
elseif ~strcmpi(semaphoreMode, 'set always')
error('Error: Semaphore mode ''%s'' not supported.\n', semaphoreMode);
end
if ischar(fileList)
% single file given
fileList = {fileList};
end
nOfFiles = length(fileList);
semaphoreCell = cell(nOfFiles, 1);
hostname = gethostname;
oldSemaphoreFilesToIgnore = {};
for fileNr = 1:nOfFiles
% get current file name
fileName = fileList{fileNr};
% check if given file is itself a semaphore file
if ~isempty(regexp(fileName, '\.semaphore\.\w+\.\d+\.mat$', 'once'))
%disp('Warning: Trying to generate a semaphore file for a semaphore file!! Will be ignored.');
semaphoreCell{fileNr, 1} = '';
continue
end
% generate semaphore file pattern of current file
semaphorePattern = [fileName, '.semaphore.*.mat'];
semaphorePatternPath = fileparts(semaphorePattern);
% initialize arrays
semaphoreFiles = {};
semaphoreStartWaitTimes = [];
% save current time
startWaitTime = mbtime;
displayWaitInfo = true;
while 1
if setSemaphoreForExistingFilesOnly || setSemaphoreForNotExistingFilesOnly
% check if target file exists
if existfile(fileName)
if setSemaphoreForNotExistingFilesOnly
% semaphores are only needed for non-existing files => exit while loop
semaphoreCell{fileNr, 1} = '';
break
end
else
if setSemaphoreForExistingFilesOnly
% semaphores are only needed for existing files => exit while loop
semaphoreCell{fileNr, 1} = '';
break
end
end
end
% get list of (possibly) existing semaphores
dirStruct = dir(semaphorePattern);
anySemaphoreExisting = false;
if ~isempty(dirStruct)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% other semaphore file(s) existing %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% check if any existing semaphore file has to be respected
for k = 1:length(dirStruct)
% get file name of current semaphore
currSemaphoreFileName = concatpath(semaphorePatternPath, dirStruct(k).name);
% check if semaphore file is included in ignore list
if ismember(currSemaphoreFileName, oldSemaphoreFilesToIgnore)
% semaphore file is included in ignore list
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%fprintf('Ignoring semaphore file %s.\n', currSemaphoreFileName);
else
% get file date
if isfield(dirStruct, 'datenum')
fileDatenum = dirStruct(k).datenum;
elseif ~isempty(dirStruct(k).date)
% in older Matlab versions, the field datenum seems not to exist
fileDatenum = datenum2(dirStruct(k).date);
else
% it happens that the info in dirStruct is damaged, file is
% checked again later in that case
anySemaphoreExisting = true;
if debugMode
fprintf('Info in dirStruct seems to be damaged.\n');
end
continue
end
% check if semaphore file is very old (by file date)
if (mbtime - (fileDatenum * 86400)) > semaphoreOldTime2
% semaphore file is very old (by file date) and will be ignored
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% add file to ignore list
oldSemaphoreFilesToIgnore{end+1} = currSemaphoreFileName; %#ok
if 0
disp(textwrap2(sprintf(['Ignoring old semaphore of file %s ',...
'(maybe not removed correctly).'], fileName)));
end
if debugMode
fprintf('Semaphore file %s added to ignore list. (1)\n', currSemaphoreFileName);
end
if removeOldSemaphores
% try to remove old semaphore file
mbdelete(currSemaphoreFileName, showWarnings, 0); %% file access %%
end
else
% check if semaphore was found before and if it was, if it
% is already existing for a long time
currSemaphoreIndex = find(strcmp(semaphoreFiles, currSemaphoreFileName), 1);
if isempty(currSemaphoreIndex) || ...
(mbtime - semaphoreStartWaitTimes(currSemaphoreIndex)) < semaphoreOldTime
% current semaphore file must be respected
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
semaphoreFiles {end+1} = currSemaphoreFileName; %#ok
semaphoreStartWaitTimes(end+1) = mbtime; %#ok
% semaphore file must be respected
anySemaphoreExisting = true;
if debugMode
fprintf('Respecting semaphore file %s.\n', currSemaphoreFileName);
end
else
% current semaphore is already existing for a long time
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% add file to ignore list
oldSemaphoreFilesToIgnore{end+1} = currSemaphoreFileName; %#ok
disp(textwrap2(sprintf('Ignoring old semaphore of file %s (maybe not removed correctly).', ...
fileName)));
if debugMode
fprintf('Semaphore file %s added to ignore list. (2)\n', currSemaphoreFileName);
end
if removeOldSemaphores
% try to remove old semaphore file
mbdelete(currSemaphoreFileName, showWarnings, 0); %% file access %%
end
end % if isempty(currSemaphoreIndex) || ...
end % if (mbtime - (fileDatenum * 86400)) > semaphoreOldTime2
end % if ismember(currSemaphoreFileName, oldSemaphoreFilesToIgnore)
end % for k=1:length(dirStruct)
% display info
if anySemaphoreExisting && displayWaitInfo && ...
(mbtime - startWaitTime) >= waitInfoTime
disp(textwrap2(sprintf('Waiting for semaphore(s) of file %s to disappear.', fileName)));
displayWaitInfo = false;
end
end % if ~isempty(dirStruct)
if debugMode
if anySemaphoreExisting
fprintf('Semaphore file found that must be respected (%s).\n', datestr(now));
else
fprintf('No semaphore file found that must be respected (%s).\n', datestr(now));
end
end
if ~anySemaphoreExisting
%%%%%%%%%%%%%%%%%%%%%%%%%%
% set own semaphore file %
%%%%%%%%%%%%%%%%%%%%%%%%%%
for attemptNr = 1:10
% build semaphore file name
[randomNr, randomStr] = generaterandomnumber; %#ok
newSemaphoreFileName = [fileName, '.semaphore.', hostname, '.', randomStr, '.mat'];
lasterror('reset');
try
generateemptyfile(newSemaphoreFileName); %% file access %%
if debugMode
fprintf('Generated semaphore file %s.\n', newSemaphoreFileName);
end
break % attemptNr = 1:10
catch
% in very very very unlikely cases two processes might have
% generated the same semaphore file name
if showWarnings
disp(textwrap2(sprintf('Warning: An error occured while generating semaphore file %s:', ...
newSemaphoreFileName)));
displayerrorstruct;
end
% wait random time and try again
pause(retryWaitTime * randomNr);
end
end
% Two semaphore files might have been created at the same time -->
% wait fixed time, then check if any other semaphore file is
% existing. This pause time unfortunately causes overhead but seems
% to be necessary.
pause(fixedWaitTime);
removeOwnSemaphore = false;
dirStruct = dir(semaphorePattern);
if length(dirStruct) > 1
for k = 1:length(dirStruct)
currSemaphoreFileName = concatpath(semaphorePatternPath, dirStruct(k).name);
if debugMode
fprintf('Semaphore file %s found. %d %d\n', currSemaphoreFileName, ...
strcmp(currSemaphoreFileName, newSemaphoreFileName), ...
ismember(currSemaphoreFileName, oldSemaphoreFilesToIgnore));
end
if (~strcmp(currSemaphoreFileName, newSemaphoreFileName)) && ...
(~ismember(currSemaphoreFileName, oldSemaphoreFilesToIgnore))
if debugMode
fprintf('Semaphore file needs to be deleted again: %s.\n', newSemaphoreFileName);
end
% at least one of the existing semaphores may not be ignored
removeOwnSemaphore = true;
break % for k = 1:length(dirStruct)
end
end
end
if ~removeOwnSemaphore
% exclusive file access is guaranteed
% save semaphore file name in output cell and leave while loop
semaphoreCell{fileNr, 1} = newSemaphoreFileName;
if debugMode
fprintf('Successfully got exclusive file access using semaphore file %s.\n', newSemaphoreFileName);
fprintf('***********************************************************\n');
end
% move to next file in input file list
break % while 1
else
if debugMode
fprintf('Removing generated semaphore file %s.\n', newSemaphoreFileName);
end
% remove own semaphore file
mbdelete(newSemaphoreFileName, showWarnings, 0); %% file access %%
% wait random time before checking everything again in while-loop
pause(retryWaitTime * rand);
end
else
% wait before checking again if semaphore files are already existing
pause(checkAgainWaitTime * rand);
end % if ~anySemaphoreExisting
end % while 1
end % for fileNr = 1:nOfFiles
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [randomNr, randomStr] = generaterandomnumber
%GENERATERANDOMNUMBER
% In very unlikely cases, it might happen that the random states of rand
% and randn are equal in two Matlab processes calling function
% SETFILESEMAPHORE. For this reason, the system and cpu time are used to
% create different random numbers even in this unlikely case.
%
% This all would not be necessary if it were possible to get some sort of a
% Matlab process ID.
nOfDigits = 8; % length of random string will be 4*nOfDigits
randNr = rand;
randnNr = mod(randn+0.5, 1);
cputimeNr = mod(cputime, 100)/100;
nowNr = mod(rem(now,1)*3600*24, 100)/100;
% random number is used for random pause after conflict
randomNr = 0.25 * (randNr + randnNr + cputimeNr + nowNr);
% random string is used for the semaphore file name
if nargout > 1
ee = 10^nOfDigits;
randomStr = sprintf(...
'%0.0f%0.0f%0.0f%0.0f', ...
ee * randNr, ...
ee * randnNr, ...
ee * cputimeNr, ...
ee * nowNr ...
);
end