1function semaphore = setfilesemaphore(fileList)
2%SETFILESEMAPHORE  Set semaphore for file access.
3%   SEMAPHORE = SETFILESEMAPHORE(FILENAME) sets a semaphore to get
4%   exclusive access on file FILE. The semaphore is realized by generating
5%   a simple Matlab data file after checking that no other semaphores are
6%   existing. The function exits if the semaphore is set. Exclusive file
7%   access is of course only guaranteed if all other Matlab processes use
8%   semaphores to access the same file.
9%
10%   The output variable SEMAPHORE is needed to correctly remove the file
11%   semaphore after file access. It is an error to call function
12%   SETFILESEMAPHORE without output arguments.
13%
14%   SEMAPHORE = SETFILESEMAPHORE(FILELIST) sets semaphores for all files
15%   given in cell array FILELIST. Note that function SETFILESEMAPHORE waits
16%   for exclusive file access on ALL files in the list before exiting.
17%
18%		Note: A semaphore older than 20 seconds is considered as invalid and
19%		will immediately be deleted.
20%
21%		Markus Buehren
22%		Last modified 13.11.2007
23%
24%   See also REMOVEFILESEMAPHORE.
25
26% set times (all in seconds)
27semaphoreOldTime = 20;
28fixedWaitTime    = 0.01;
29checkWaitTime    = 0.1;
30maxRandomTime    = 0.3;
31
32if nargout ~= 1
33	error('Function %s must be called with one output argument!', mfilename);
34end
35
36if ischar(fileList)
37	% single file given
38	fileList = {fileList};
39end
40
41nOfFiles = length(fileList);
42semaphore = cell(nOfFiles, 1);
43for fileNr = 1:nOfFiles
44
45	fileName = fileList{fileNr};
46
47	% check if given file is itself a semaphore file
48	if ~isempty(regexp(fileName, '\.semaphore\.\w+\.\d{32}\.mat$', 'once'))
49		semaphore{fileNr, 1} = '';
50		continue
51	end
52
53	% generate semaphore file name and pattern
54	[ignore, randomStr] = generaterandomnumber; %#ok
55	semaphoreFileName = [fileName, '.semaphore.', randomStr, '.mat'];
56	semaphorePattern  = [fileName, '.semaphore.', '*',       '.mat'];
57	semaphorePatternPath = fileparts(semaphorePattern);
58
59	while 1
60		dirStruct = dir(semaphorePattern);
61
62		if ~isempty(dirStruct)
63			% other semaphore file(s) existing
64			% check if any semaphore is very old
65			allSemaphoresOld = true;
66			for k=1:length(dirStruct)
67				if now - datenum2(dirStruct(k).date) > semaphoreOldTime / (3600*24)
68					% remove semaphore
69					disp(sprintf('Warning: Removing old semaphore file %s.', dirStruct(k).name));
70					oldSemaphoreFileName = fullfile(semaphorePatternPath, dirStruct(k).name);
71					if exist(oldSemaphoreFileName, 'file')
72						delete(oldSemaphoreFileName);
73					end
74				else
75					allSemaphoresOld = false;
76				end
77			end
78
79			if allSemaphoresOld
80				continue
81			end
82
83			% wait before checking again
84			pause(checkWaitTime);
85
86		else
87			% set own semaphore file
88			try
89 				save(semaphoreFileName, 'randomStr');
90			catch
91				disp(sprintf('An error occured while accessing semaphore file %s:', semaphoreFileName));
92				displayerrorstruct;
93
94				% in very very very unlikely cases two processes might have
95				% generated the same semaphore file name
96				[randomNr, randomStr] = generaterandomnumber; %#ok
97				pause(0.2+maxRandomTime * randomNr);
98				semaphoreFileName = [fileName, '.semaphore.', randomStr, '.mat'];
99				save(semaphoreFileName, 'randomStr');
100			end
101
102			% wait fixed time
103			pause(fixedWaitTime);
104
105			% in very unlikely cases, two semaphore files might have been created
106			% at the same time
107			dirStruct = dir(semaphorePattern);
108			if length(dirStruct) > 1
109				% remove own semaphore file
110				delete(semaphoreFileName);
111
112				% wait RANDOM time before checking again
113				pause(maxRandomTime * generaterandomnumber);
114
115			else
116				% exclusive file access is guaranteed
117				% save semaphore file name and leave while loop
118				semaphore{fileNr, 1} = semaphoreFileName;
119				break
120			end
121		end % if ~isempty(dirStruct)
122	end % while 1
123end % for fileNr = 1:nOfFiles
124
125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126function [randomNr, randomStr] = generaterandomnumber
127%GENERATERANDOMNUMBER
128%   in very unlikely cases, it might happen that the random states of rand
129%   and randn are equal in two Matlab processes calling function
130%   SETSEMAPHORE. For this reason, the system and cpu time are used to create
131%   different random numbers even in this unlikely case.
132
133nOfDigits = 8; % length of random string will be 4*nOfDigits
134
135randNr    = rand;
136randnNr   = mod(randn+0.5, 1);
137cputimeNr = mod(cputime, 100)/100;
138nowNr     = mod(rem(now,1)*3600*24, 100)/100;
139
140% random number is used for random pause after conflict
141randomNr = 0.25 * (randNr + randnNr + cputimeNr + nowNr);
142
143% random string is used for the semaphore file name
144if nargout > 1
145	ee = 10^nOfDigits;
146	randomStr = [...
147		num2str(ee * randNr,    '%.0f'), ...
148		num2str(ee * randnNr,   '%.0f'), ...
149		num2str(ee * cputimeNr, '%.0f'), ...
150		num2str(ee * nowNr,     '%.0f'), ...
151		];
152end
153
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155function dateNr = datenum2(dateStr)
156%DATENUM2  Return serial date number.
157%   DATENUM2(STR) returns the serial date number for the given date string
158%   STR. Function DATENUM2 is a wrapper for DATENUM which replaces german
159%   month abbreviations like "Okt" by the english versions like "Oct"
160%   before forwarding the string to function DATENUM.
161%
162%   Function DATENUM2 first tries to build a date vector from the given
163%   string for performance reasons. However, only date format 0
164%   (dd-mmm-yyyy HH:MM:SS) is supported for this. If the given date string
165%   is in a different format, the string is forwarded to function DATENUM.
166
167tokenCell = regexp(dateStr, '(\d+)-(\w+)-(\d+) (\d+):(\d+):(\d+)', 'tokens');
168tokenCell = tokenCell{1};
169if length(tokenCell) == 6
170	% supported date format (at least it seems so)
171
172	% get month
173	switch tokenCell{2}
174		case 'Jan'
175			month = 1;
176		case 'Feb'
177			month = 2;
178		case {'Mar', 'M�r', 'Mrz'}
179			month = 3;
180		case 'Apr'
181			month = 4;
182		case {'May', 'Mai'}
183			month = 5;
184		case 'Jun'
185			month = 6;
186		case 'Jul'
187			month = 7;
188		case 'Aug'
189			month = 8;
190		case 'Sep'
191			month = 9;
192		case {'Oct', 'Okt'}
193			month = 10;
194		case 'Nov'
195			month = 11;
196		case {'Dec', 'Dez'}
197			month = 12;
198		otherwise
199			% obviously the data format is not supported
200			disp('Date format not supported (1)');
201			dateNr = datenum(translatedatestr(dateStr));
202			return
203	end
204
205	dateVec = [...
206		str2double(tokenCell{3}), ... % year
207		month, ...                    % month
208		str2double(tokenCell{1}), ... % day
209		str2double(tokenCell{4}), ... % hours
210		str2double(tokenCell{5}), ... % minutes
211		str2double(tokenCell{6}), ... % seconds
212		];
213	dateNr = datenum(dateVec);
214
215else
216	% unknown date format
217	dateNr = datenum(translatedatestr(dateStr));
218end
219
220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221function dateStr = translatedatestr(dateStr)
222%TRANSLATEDATESTR  Translate german date string to english version.
223%		STR = TRANSLATEDATESTR(STR) converts a german date string like
224%		  13-M�r-2006 15:55:00
225%		to the english version
226%		  13-Mar-2006 15:55:00.
227%		This is needed on some systems if function DIR returns german date
228%		strings.
229%
230%		Markus Buehren
231%
232%		See also DATENUM2.
233
234dateStr = strrep(dateStr, 'Mrz', 'Mar');
235dateStr = strrep(dateStr, 'M�r', 'Mar');
236dateStr = strrep(dateStr, 'Mai', 'May');
237dateStr = strrep(dateStr, 'Okt', 'Oct');
238dateStr = strrep(dateStr, 'Dez', 'Dec');
239
240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241function displayerrorstruct(errorStruct)
242%DISPLAYERRORSTRUCT  Display structure returned by function lasterror.
243%		DISPLAYERRORSTRUCT displays the structure returned by function
244%		LASTERROR. Useful when catching errors.
245%
246%		Markus Buehren
247%
248%   See also LASTERROR.
249
250if nargin == 0
251	errorStruct = lasterror;
252end
253
254disp(errorStruct.message);
255errorStack = errorStruct.stack;
256for k=1:length(errorStack)
257	disp(sprintf('Error in ==> %s at %d.', errorStack(k).name, errorStack(k).line));
258end