Trying to let multiple processes across different Linux nodes write to a shared CSV file on a Windows shared folder.
I tested if it works by launching 2 processes on separate nodes, each appending 10k lines to a file. Then I check if the line count is 20k. Unfortunately, the line count is almost always less than that. I'm using Linux 3.0.51 and Windows Server 2008 R2. However, locking across multiple processes within 1 node does work with flock(), but not fcntl().
Here are some other things I tried:
Use NFS - does work! (sometimes line count is 19999)
Use Linux kernel 4.10 and mount as SMB 3.02 - doesn't work
Using Samba as the server - doesn't work, even with strict locking = yes as described here:
https://www.samba.org/samba/docs/man/Samba-HOWTO-Collection/locking.html
Has anyone got this to work or know it can't be done (and for what configuration)? Here's my test program:
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>
#include <boost/interprocess/sync/file_lock.hpp>
// method 0: fcntl() - works for NFS and across multiple nodes, but not for CIFS/SMB
// method 1: flock() - works for SMB within 1 node, but not accross multiple nodes
// method 2: Boost (internally uses fcntl)
#define METHOD 1
using namespace std;
class FileLock
{
public:
#if METHOD == 2
FileLock(const char *path) : lock(path)
{
}
#else
FileLock(int f)
{
file = f;
#if METHOD == 0
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
#endif
}
#endif
bool Lock()
{
#if METHOD == 0
lock.l_type = F_WRLCK;
return fcntl(file, F_SETLKW, &lock) == 0;
#elif METHOD == 1
return flock(file, LOCK_EX) == 0;
#elif METHOD == 2
lock.lock();
return true;
#endif
}
bool Unlock()
{
#if METHOD == 0
lock.l_type = F_UNLCK;
return fcntl(file, F_SETLKW, &lock) == 0;
#elif METHOD == 1
return flock(file, LOCK_UN) == 0;
#elif METHOD == 2
lock.unlock();
return true;
#endif
}
int file;
#if METHOD == 0
struct flock lock;
#elif METHOD == 2
boost::interprocess::file_lock lock;
#endif
};
int main(int argc, char **argv)
{
int repeats = 100;
double interval = 0.1;
char message[256];
sprintf(message, "testing 123\n");
if (argc >= 2)
repeats = atoi(argv[1]);
if (argc >= 3)
interval = atof(argv[2]);
if (argc >= 4)
{
sprintf(message, "%s\n", argv[3]);
}
FILE *f = fopen("a.txt", "a");
if (f == NULL)
{
cout << "can't open" << endl;
return 1;
}
#if METHOD == 2
FileLock lock("a.txt");
#else
FileLock lock(fileno(f));
#endif
for (int i = 0; i < repeats; ++i)
{
if (!lock.Lock())
cout << "error locking " << strerror(errno) << "\n";
// file in append mode will automatically write to end, but does it work when there are multiple writers?
fseek(f, 0, SEEK_END);
fwrite(message, 1, strlen(message), f);
if (!lock.Unlock())
cout << "error unlocking\n";
usleep(interval * 1000000);
}
fclose(f);
return 0;
}