6

I am looking for a simple already implemented solution for atomically creating a file lock in MATLAB.

Something like:

file_lock('create', 'mylockfile'); %this will block until it creates the lock file.
file_lock('remove', 'mylockfile'); %this will remove the lock file:

This question has already been asked several times, with some proposed solution ideas (such as using Java FileLock), but I didn't find a simple already implemented solution.

Are you aware of such an implemented solution?

Notes:

Amro
  • 123,847
  • 25
  • 243
  • 454
David Portabella
  • 12,390
  • 27
  • 101
  • 182
  • 4
    I hate to be a wet blanket, but this is extremely difficult to get correct in a general manner, especially for network files. File locking is highly system dependent. There will be no simple, already implemented solution that isn't broken. (Not hard to write something that "seems to mostly work"; hard to write something that won't fail in production somewhere.) Let's step back a moment: what are you trying to synchronize access to? Is it the file contents, or do the files represent some other resource? What platforms are you targeting? How "correct" do you need the exclusion to be? – Andrew Janke Aug 10 '10 at 21:31

5 Answers5

6

I've settled on a pretty simple solution for combining error/logging messages from multiple worker threads into a single file. Every time I want to write to that file, I first write the output to the thread's own temporary file. Next, I append that temporary file to the "master" log file using flock. Skipping over some details here, the idea is:

fid=fopen(threadtemp, 'w');
fprintf(fid, 'Error message goes here');
fclose(fid);

runme = sprintf('flock -x %s -c ''cat %s >> %s''', LOGFILE, threadtemp, LOGFILE);
system(runme);

See the flock man page for details, but the call above is acquiring an eXclusive lock on the logfile, running the provided Command under the lock, and then releasing it.

This obviously only works if you're on a system which has flock (Linux/OS X, and only certain types of file systems at that) and you're doing something that can be done from the command line, but I'd bet that it's a pretty common use-case.

Matt Krause
  • 1,113
  • 14
  • 31
  • just wondering, why do you go through the step of creating the temporary file? why not just something like system('flock -x logfile -c '' echo errormessage >> logfile '' ')? thanks – Grittathh Apr 09 '13 at 19:07
  • No particular reason--your approach should work just fine. I think my some of my original functions took filenames to log to, so this let me use them without changes, but it's mostly vestigial, I guess. The only real trick is using system() to call flock. – Matt Krause Apr 09 '13 at 19:45
1

Depending on which Java version you're using, perhaps this will work (translated from: http://www.javabeat.net/2007/10/locking-files-using-java/)

classdef FileLock < handle
    properties (Access = private)
        fileLock = []
        file
    end

    methods
        function this = FileLock(filename)
            this.file = java.io.RandomAccessFile(filename,'rw');
            fileChannel = this.file.getChannel();
            this.fileLock = fileChannel.tryLock();
        end

        function val = hasLock(this)
            if ~isempty(this.fileLock) && this.fileLock.isValid()
                val = true;
            else
                val = false;
            end
        end

        function delete(this)
            this.release();
        end

        function release(this)
            if this.hasLock
                this.fileLock.release();
            end
            this.file.close
        end
    end
end

Usage would be:

lock = FileLock('my_lock_file');
if lock.hasLock
    %// do something here
else
    %// I guess not
end
%// Manually release the lock, or just delete (or let matlab clean it up)

I like the obj wrapping pattern for IO so that releasing happens even in exceptions

EDIT: The file ref must be kept around and manually closed or you won't be able to edit this. That means this code is only really useful for pure lock files, I think.

Nathan Donnellan
  • 573
  • 3
  • 11
1

If you only need to run on OS X and Linux (not Windows), you can use the following:

pathLock='/tmp/test.lock'

% Try to create and lock this file.
% In my case I use -r 0 to avoid retrying
% You could use -r -1 to retry forever, or for a particular amount of time,
% etc, see `man lockfile` for details.
if ~system(sprintf('lockfile -r 0 %s',pathLock))
    % We succeeded, so perform some task which needs to be serialized.
    % runSerializedTask()
    % Now remove the lockfile
    system(sprintf('rm -f %s',pathLock));
end
nonagon
  • 3,271
  • 1
  • 29
  • 42
  • 1
    Note that there is a lot of discussion about whether approaches like this are safe on networked filesystems. The lockfile man page says it's "NFS-resistant", but I'm not sure what that means :) or whether it covers all of the concerns about this (they get quite complicated!) – nonagon Oct 27 '15 at 16:09
0

Write to a new file, then rename it. Renaming is an atomic operation, and all the new content will become visible at once.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • This idea was proposed also in http://groups.google.com/group/comp.soft-sys.matlab/browse_thread/thread/eb4ebeb8700ea440/13bd6710e8429a70?lnk=raot but it turns out that unix's rename is not atomic (first it deletes the dest file, then source file renamed), and matlab uses unix's rename in unix, so this solution does not work neither. – David Portabella Aug 10 '10 at 17:10
  • 1
    Just read that thread. The consensus seems to be that MatLab's `movefile` command does more than just a single `rename` call, so `movefile` isn't atomic. The SUS is pretty clear that `rename` is atomic on unix (of course, if you're using a Unix-like clone that doesn't adhere to the spec that's a different story). http://opengroup.org/onlinepubs/007908775/xsh/rename.html – Ben Voigt Aug 10 '10 at 19:20
0

At the end I did one implementation based on two consecutive tests (movefile, and verify the contents of the moved file).

not very well written, but it works for now for me.

+++++ file_lock.m ++++++++++++++++++++++++

function file_lock(op, filename)
%this will block until it creates the lock file:
%file_lock('create', 'mylockfile') 
%
%this will remove the lock file:
%file_lock('remove', 'mylockfile')


% todo: verify that there are no bugs

filename = [filename '.mat'];

if isequal(op, 'create')
  id = [tempname() '.mat'] 
  while true
    save(id, 'id');
    success = fileattrib(id, '-w');
    if success == 0; error('fileattrib'); end

    while true
      if exist(filename, 'file');        %first test
        fprintf('file lock exists(1). waiting...\n');
        pause(1); 
        continue;
      end
      status = movefile(id, filename);   %second test
      if status == 1; break; end
      fprintf('file lock exists(2). waiting...\n');
      pause(1);
    end

    temp = load(filename, 'id');         % third test.
    if isequal(id, temp.id); break; end

    fprintf('file lock exists(3). waiting...\n');
    pause(1)
  end

elseif isequal(op, 'remove')
  %delete(filename);
  execute_rs(@() delete(filename));

else
  error('invalid op');
end



function execute_rs(f)
while true
  try 
    lastwarn('');
    f();
    if ~isequal(lastwarn, ''); error(lastwarn); end  %such as: Warning: File not found or permission denied 
    break;
  catch exception
    fprintf('Error: %s\n.Retrying...\n', exception.message);
    pause(.5);
  end
end

++++++++++++++++++++++++++++++++++++++++++

David Portabella
  • 12,390
  • 27
  • 101
  • 182