How to open file in exclusive mode in C++ - c++

I am implementing some file system in C++. Up to now I was using fstream but I realized that it is impossible to open it in exclusive mode. Since there are many threads I want to allow multiple reads, and when opening file in writing mode I want to open the file in exclusive mode?
What is the best way to do it? I think Boost offers some features. And is there any other possibility? I would also like to see simple example. If it is not easy / good to do in C++ I could write in C as well.
I am using Windows.

On many operating systems, it's simply impossible, so C++
doesn't support it. You'll have to write your own streambuf.
If the only platform you're worried about is Windows, you can
possibly use the exclusive mode for opening that it offers.
More likely, however, you would want to use some sort of file
locking, which is more precise, and is available on most, if not
all platforms (but not portably—you'll need LockFileEx
under Windows, fcntl under Unix).
Under Posix, you could also use pthread_rwlock. Butenhof
gives an implementation of this using classical mutex and
condition variables, which are present in C++11, so you could
actually implement a portable version (provided all of the
readers and writers are in the same process—the Posix
requests will work across process boundaries, but this is not
true for the C++ threading primitives).

if your app only works on Windows, the Win32 API function CreateFile() is your choice.
For example:
HANDLE hFile = ::CreateFileW(lpszFileFullPathName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);

If you are open to using boost, then I would suggest you use the file_lock class. This means you want to keep the filename of the files you open/close because fstream does not do so for you.
They have two modes lock() that you can use for writing (i.e. only one such lock at a time, the sharable lock prevents this lock too) and lock_sharable() that you can use for reading (i.e. any number of threads can obtain such a lock).
Note that you will find it eventually complicated to manage both, read and write, in this way. That is, if there is always someone to read, the sharable lock may never get released. In that case, the exclusive lock will never be given a chance to take....
// add the lock in your class
#include <boost/interprocess/sync/file_lock.hpp>
class my_files
{
...
private:
...
boost::file_lock m_lock;
};
Now when you want to access a file, you can lock it one way or the other. If the thread is in charge of when they do that, you could add functions for the user to have access to the lock. If your implementation of the read and write functions in my_files are in charge, you want to get a stack based object that locks and unlocks for you (RAII):
class safe_exclusive_lock
{
public:
safe_exclusive_lock(file_lock & lock)
: m_lock_ref(lock)
{
m_lock_ref.lock();
}
~safe_exclusive_lock()
{
m_lock_ref.unlock();
}
private:
file_lock & m_lock_ref;
};
Now you can safely lock the file (i.e. you lock, do things that may throw, you always unlock before exiting your current {}-block):
ssize_t my_files::read(char *buf, size_t len)
{
safe_exclusive_lock guard(m_lock);
...your read code here...
return len;
} // <- here we get the unlock()
ssize_t my_files::write(char const *buf, size_t len)
{
safe_exclusive_lock guard(m_lock);
...your write code here...
return len;
} // <- here we get the unlock()
The file_lock uses a file, so you will want to have the fstream file already created whenever the file_lock is created. If the fstream file may not be created in your constructor, you probably will want to transform the m_lock variable in a unique pointer:
private:
std::unique_ptr<file_lock> m_lock;
And when you reference it, you now need an asterisk:
safe_exclusive_lock guard(*m_lock);
Note that for safety, you should check whether the pointer is indeed allocated, if not defined, it means the file is not open yet so I would suggest you throw:
if(m_lock)
{
safe_exclusive_lock guard(*m_lock);
...do work here...
}
else
{
throw file_not_open();
}
// here the lock was released so you cannot touch the file anymore
In the open, you create the lock:
bool open(std::string const & filename)
{
m_stream.open(...);
...make sure it worked...
m_lock.reset(new file_lock(filename));
// TODO: you may want a try/catch around the m_lock and
// close the m_stream if it fails or use a local
// variable and swap() on success...
return true;
}
And do not forget to release the lock object in your close:
void close()
{
m_lock.reset();
}

Well you can manually prevent yourself from opening a file if it has been opened in write mode already. Just keep track internally of which files you've opened in write mode.
Perhaps you could hash the filename and store it in a table upon open with write access. This would allow fast lookup to see if a file has been opened or not.

You could rename the file, update it under the new name, and rename it back. I've done it, but it's a little heavy.

Since C++17 there are two options:
In C++23 by using the openmode std::ios::noreplace.
In C++17 by using the std::fopen mode x (exclusive).
Note: The x mode was added to c in C11.
C++23 and later:
#include <cerrno>
#include <cstring>
#include <fstream>
#include <iostream>
int main() {
std::ofstream ofs("the_file", std::ios::noreplace);
if (ofs) {
std::cout << "success\n";
} else {
std::cerr << "Error: " << std::strerror(errno) << '\n';
}
}
Demo
C++17 and later:
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
struct FILE_closer {
void operator()(std::FILE* fp) const { std::fclose(fp); }
};
// you may want overloads for `std::filesystem::path`, `std::string` etc too:
std::ofstream open_exclusively(const char* filename) {
bool excl = [filename] {
std::unique_ptr<std::FILE, FILE_closer> fp(std::fopen(filename, "wx"));
return !!fp;
}();
auto saveerr = errno;
std::ofstream stream;
if (excl) {
stream.open(filename);
} else {
stream.setstate(std::ios::failbit);
errno = saveerr;
}
return stream;
}
int main() {
std::ofstream ofs = open_exclusively("the_file");
if (ofs) {
std::cout << "success\n";
} else {
std::cout << "Error: " << std::strerror(errno) << '\n';
}
}
Demo

Related

In C++, how to detect that file has been already opened by own process?

I need to create a logger facility that outputs from different places of code to the same or different files depending on what the user provides. It should recreate a file for logging if it is not opened. But it must append to an already opened file.
This naive way such as
std::ofstream f1(“log”);
f1 << "1 from f1\n";
std::ofstream f2(“log”);
f2 << "1 from f2\n";
f1 << "2 from f1\n";
steals stream and recreates the file. Log contains
1 from f2
With append, it will reuse file, but the second open steals stream from f1.
Log contains
1 from f1
1 from f2
Try to guess which files will be used and open them all in the very begging will work but may create a lot of files that are not actually used.
Open for append and closing on each logging call would be an almost working solution, but it seems to be a slow solution due to a lot of system calls and flushing on each logging action.
I’m going to create a static table of opened files, hoping that std::filesystem::canonical
will work in all of my cases. But as far as I understand such a table should already exist somewhere in the process.
I've read that in Fortran people can check if a file was opened using inquire.
Check whether file has been opened already
But that answer did not give me any insight on how to achieve the same with С/C++.
Update
A scratch of the logger with a "static" table of open logs can look like
//hpp
class Logger {
static std::mutex _mutex;
static std::unordered_map<std::string, std::ofstream> _openFiles;
std::ostream& _appender;
std::ostream& _createAppender(const std::filesystem::path& logPath);
public:
Logger(const std::filesystem::path& logPath):
_appender(_createAppender(logPath)) {
}
template<class... Args>
void log(const Args&... args) const {
std::scoped_lock<std::mutex> lock(_mutex);
(_appender << ... << args);
}
};
//cpp
#include "Logger.hpp"
std::mutex Logger::_mutex;
std::unordered_map<std::string, std::ofstream> Logger::_openFiles;
std::ostream& Logger::_createAppender(const std::filesystem::path& logPath) {
if (logPath.empty()) return std::cout;
const auto truePath{std::filesystem::weakly_canonical(logPath).string()};
std::scoped_lock<std::mutex> lock(_mutex);
const auto entry{_openFiles.find(truePath)};
if (entry != _openFiles.end()) return entry->second;
_openFiles.emplace(truePath, logPath);
std::ostream& stream{_openFiles[truePath]};
stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);
return stream;
}
maybe it will help someone.
Yet, I still wonder if it is possible to get table mapping handles/descriptors from OS mentioned by #yzt, and will accept as an answer if someone explains how to do that inside the program.
So here is a simple Linux specific code that checks whether a specified target file is open by the current process (using --std=c++17 for dir listing but any way can be used of course).
#include <string>
#include <iostream>
#include <filesystem>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
bool is_open_by_me(const std::string &target)
{
char readlinkpath[PATH_MAX];
std::string path = "/proc/" + std::to_string(getpid()) + "/fd";
for (const auto & entry : std::filesystem::directory_iterator(path)) {
readlink(entry.path().c_str(), readlinkpath, sizeof(readlinkpath));
if (target == readlinkpath)
return true;
}
return false;
}
Simply list the current pid's open handles via proc, then use readlink function to resolve it to the actual file name.
That is the best way to do it from the userspace I know. This information is not known by the process itself, it is known by the kernel about the process, hence the process has to use various tricks, in this case parsing procfs, to access it.
If you want to check whether a different process hold an open handle to a file, you will have to parse all the procfs for all processes. That may not be always possible since other processes may be run by different users.
All that said - in your specific case, when you are the one owner, opening and closing the files - maintaining a table of open handles is a much cleaner solution.

boost find in shared-memory method get stuck in c++ multi-process project

I am using boost's ipc library for saving complex object, include images, in shared memory, used by several processes. Let's call this object MyImage. The shared memory is a circular buffer saving several MyImage objects at a time.
In my code there are two (or more) processes writing to a segment in a shared memory, and another one reading from it. This flow works as expected, but after the reader process is done, or crashed, when it tries to open the same object in shared memory again it get stuck on find method, while the writer processes are still running fine.
I tried to understand which race condition could cause this, but couldn't find any explanation in my code, or in boost's documentation.
Here is a simple code exampled the problem in my project:
The Writer process:
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/circular_buffer.hpp>
using namespace std;
namespace bip = boost::interprocess;
static const char *const PLACE_SHM_NAME = "PlaceInShm";
static const char *const OBJECT_SHM_NAME = "ObjectInShm";
static const char *const PUSH_POP_LOCK = "push_pop_image_lock";
static const int IMAGES_IN_BUFFER = 20;
static const int OBJECT_SIZE_IN_SHM = 91243520;
class MyImage;
typedef bip::managed_shared_memory::segment_manager SegmentManagerType;
typedef bip::allocator<void, SegmentManagerType> MyImageVoidAllocator;
typedef bip::deleter<MyImage, SegmentManagerType> MyImageDeleter;
typedef bip::shared_ptr<MyImage, MyImageVoidAllocator, MyImageDeleter> MyImageSharedPtr;
typedef bip::allocator<MyImageSharedPtr, bip::managed_shared_memory::segment_manager> MyImageShmemAllocator;
typedef boost::circular_buffer<MyImageSharedPtr, MyImageShmemAllocator> MyImageContainer;
MyImageSharedPtr GetMyImage() {
// some implementation
return nullptr;
}
int main(int argc, char *argv[]) {
MyImageContainer *my_image_data_container;
try {
bip::named_mutex open_lock{bip::open_or_create, OPEN_SHM_LOCK};
bip::managed_shared_memory image_segment = bip::managed_shared_memory(bip::open_or_create, PLACE_SHM_NAME, OBJECT_SIZE_IN_SHM);
my_image_data_container = image_segment.find_or_construct<MyImageContainer>(OBJECT_SHM_NAME)(IMAGES_IN_BUFFER, image_segment.get_segment_manager());
} catch (boost::interprocess::interprocess_exception &e) {
exit(1);
}
boost::interprocess::named_mutex my_image_mutex_ptr(boost::interprocess::open_or_create, PUSH_POP_LOCK);
while (true) {
MyImageSharedPtr img = GetMyImage();
my_image_mutex_ptr.lock();
my_image_data_container->push_back(img);
my_image_mutex_ptr.unlock();
usleep(1000);
}
}
The Reader process:
int main(int argc, char *argv[]) {
MyImageContainer *my_image_data_container;
try {
bip::named_mutex open_lock{bip::open_only, OPEN_SHM_LOCK};
bip::scoped_lock<bip::named_mutex> lock(open_lock, bip::try_to_lock);
bip::managed_shared_memory image_segment = bip::managed_shared_memory(bip::open_only, PLACE_SHM_NAME);
my_image_data_container = image_segment.find<MyImageContainer>(OBJECT_SHM_NAME).first;
} catch (boost::interprocess::interprocess_exception &e) {
exit(1);
}
boost::interprocess::named_mutex my_image_mutex_ptr(boost::interprocess::open_or_create, PUSH_POP_LOCK);
while (true) {
if (my_image_data_container->size() == 0) {
continue;
}
MyImage *img;
my_image_mutex_ptr.lock();
img = &(*my_image_data_container->at(0));
my_image_data_container->pop_front();
my_image_mutex_ptr.unlock();
// do stuff with img
usleep(1000);
}
}
The flow to reproduce the bug:
Run two processes of the Writer code.
Run one process of the Reader code.
kill the Reader process.
run the Reader process again.
At the second run, the process is stuck in the line image_segment.find<MyImageContainer>(OBJECT_SHM_NAME).first; while the Writer processes are fine.
Important to mention that each Writer process have a unique id, and write to the buffer in the shared memory only int(IMAGES_IN_BUFFER / NUMBER_OF_WRITERS) images starting from the index as his id.
For example, I have two Writers with id 0 and id 1, IMAGES_IN_BUFFER=20, then Writer 0 will write to indexes 0-9 and Writer 1 to 10-19.
Some of my debugging process:
I tried to open the shared memory in a separate thread, using the future object, and set a timeout of few seconds. But the whole process is still stuck.
When I kill the process after it is stuck, and re-run it, it never succeed again, unless I delete the object from shared memory and re-run all of the processes, include the Writers.
Usually when running with one Writer I couldn't reproduce the bug, but I can't say for sure.
It is not consistent, meaning I can't tell when it will get stuck and when not.
Maybe the object in the shared memory is corrupted somehow, while the Reader processes is crashing, and then to while reopen it, it fails. In that case I expect that boost will raise an exception not hang.
When the process exit gracefully, with exit code 0, it can happen as well.
Waiting to hear some opinions about what can be the cause of the process getting stuck.
Thanks in advance!
I see many issues with your code. Beyond that, there is also a known limitation. So let's start with that
Robustness of Interprocess Mutexes
First off, it's a wellknown issue with the library that there are no robust interprocess mutexes (portably):
How do I take ownership of an abandoned boost::interprocess::interprocess_mutex?
Boost interprocess mutexes and checking for abandonment etc.
But also see https://www.boost.org/doc/libs/1_76_0/boost/interprocess/detail/robust_emulation.hpp I think there are alternatives using file locks on some platforms/filesystems.
So, the best you can probably do is indeed to do a timed wait and have a "forced clear" option that you can manually engage when you know it is safe to do so.
Code Issues and Review
That said, there are some issues with the code, and some things you may improve to get things less brittle.
You mentioned crashing. This goes without saying: crashes are going to break invariants, avoid them.
This may include having a proper interrupt signal handler to make sure that you properly shut down.
You never lock the open lock in the writer path. This is and obvious problem that should be fixed.
In the reader path you issue a try_lock but never seem to verify that it succeeded.
In the code as shown, you're using my_image_data_container after the shared memory segment is destructed. This, by definition, will always be Undefined Behaviour
You are not using a RAII-enabled lock guard for the push/pop mutex (my_image_mutex_ptr). This means that it is not exception safe and will - again - cause locks to get stuck on exceptions.
In general, you seem to be confusing lockable primitives with locks. I'd suggest renaming the objects (open_lock -> open_mutex, my_image_mutex_ptr (?!) -> modify_mutex) to avoid such confusion.
I would probably suggest using the same mutex for both opening and modification (after all, creating the segment is not really allowed during modification, is it?). Alternatively, consider using an unnamed interprocess mutex inside the shared segment to remove unnecessary SHM namespace pollution. One less lock that could potentially be stuck, even after removing the shared memory segment itself!).
The ->empty() check is a data race:
it doesn't lock the modify-mutex so the container might be written to concurrently
it doesn't hold that same lock, so right after returning from empty() the return value is no longer reliable, as something may have been modified in the mean time
In C++, a data race is also invoking Undefined Behaviour
There's big problems here:
img = &(*container->at(0));
This dereferences the shared pointer, retaining only the raw pointer. However, the next line pop_front()s from the container, so the shared pointer is removed, potentially (likely, given the code shown) destroying the image.
Just don't lose the refcount, and use the shared pointer.
Many names can be improved for readability. You'd typically think "computers don't care", but humans do. And all the micro-confusions compound, which probably explains 50% of the bugs uncovered in this post.
Some loose ends (exceptions are best caught by const&; you should check the bool on find<>(), container->at(0) can be spelled as container->front() etc.)
Counter Demo
Here's a version of the code reviewed for the above and written with a a bit more modern C++ style. The writer and reader are in a single main now (which you can switch using an arbitrary command line argument).
Live On Coliru
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/circular_buffer.hpp>
#include <iostream>
#include <mutex>
#include <thread>
namespace bip = boost::interprocess;
using namespace std::chrono_literals;
constexpr char const* OPEN_SHM_LOCK = "OPEN_SHM_LOCK";
static const char* const SHM_NAME = "PlaceInShm";
static const char* const OBJ_NAME = "ObjectInShm";
static const char* const PUSH_POP_LOCK = "push_pop_image_lock";
static const int BUF_CAPACITY = 20;
static const int SHM_SIZE = 91243520;
using Segment = bip::managed_shared_memory;
using Mgr = Segment::segment_manager;
class MyImage{};
template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyDeleter = bip::deleter<MyImage, Mgr>;
using SharedImage = bip::shared_ptr<MyImage, Alloc<MyImage>, MyDeleter>;
using Container = boost::circular_buffer<SharedImage, Alloc<SharedImage>>;
SharedImage GetMyImage() {
// some implementation
return {};
}
int main(int argc, char**) try {
bool const isWriter = (argc == 1);
std::cout << (isWriter? "Writer":"Reader") << std::endl;
// extract variable part to reduce code duplication
auto find_container = [isWriter](Segment& smt) {
if (isWriter)
return smt.find_or_construct<Container>(OBJ_NAME)(
BUF_CAPACITY, smt.get_segment_manager());
auto [container, ok] = smt.find<Container>(OBJ_NAME);
assert(ok); // TODO proper error handling?
return container;
};
bip::named_mutex open_mutex{bip::open_or_create, OPEN_SHM_LOCK};
if (std::unique_lock open_lk{open_mutex, std::try_to_lock}) {
Segment smt(bip::open_or_create, SHM_NAME, SHM_SIZE);
auto container = find_container(smt);
open_lk.unlock();
bip::named_mutex modify_mutex(bip::open_or_create, PUSH_POP_LOCK);
while (isWriter) {
SharedImage img = GetMyImage();
{
std::unique_lock lk(modify_mutex);
container->push_back(img);
}
std::cout << "Pushed" << std::endl;
std::this_thread::sleep_for(1s);
}
while (not isWriter) {
SharedImage img;
if (std::unique_lock lk(modify_mutex); !container->empty()) {
img = std::move(container->front());
container->pop_front();
} else {
continue;
}
// if (img)
{
// do stuff with img
std::cout << "Popped" << std::endl;
}
std::this_thread::sleep_for(1s);
}
} else {
std::cout << "Failed to acquire open lock" << std::endl;
}
} catch (bip::interprocess_exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
exit(1);
}
Which on my system works well enough. I left as an exercise for the reader: replacing the modify-lock and adding signal handlers for shutdown.
Recorded Demo
Here's a simple recorded demo that demonstrates it working as expected on my system, using various numbers of readers/writers:

Program abort hangs the named mutex

I have several processes but only one should be running at the time. This means that let's say the Process1 is running and if the Process2 get launched, then Process2 should wait until Process1 is complete. I am considering the boost named_mutex for this purpose. In order to avoid a scenario where mutex may not get released if some exception is thrown, it looks like boost::lock_guard could be useful. I came up with the following simplified version of the code.
#include <iostream>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/thread.hpp>
#include <chrono>
#include <thread>
using namespace boost::interprocess;
#pragma warning(disable: 4996)
int main()
{
std::cout << "Before taking lock" << std::endl;
named_mutex mutex(open_or_create, "some_name");
boost::lock_guard<named_mutex> guard(mutex) ;
// Some work that is simulated by sleep
std::cout << "now wait for 10 second" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Hello World";
}
So far, so good. When this program is running, I hit Ctl+C so the program gets aborted (kind of simulation of program crashed, unhandled exception etc). After that when I run the application, the program gets hung on the following line of code.
named_mutex mutex(open_or_create, "some_name");
boost::lock_guard<named_mutex> guard(mutex) ;
If I change the mutex name, then it works fine without getting hung. However, it looks like mutex named some_name is somehow "remembered" on the machine in some sort of bad state. This results in any application that tries to acquire a mutex with name some_name gets hung on this line of code. If I change this mutex name to let' say some_name2, the program works fine again.
Can someone please explain what is causing this behavior?
How can I reset the behavior for this particular mutex?
Most importantly, how to avoid this scenario in a real application?
As explained in this answer to the question linked by #ppetraki above, boost::interprocess:named_mutex, unfortunately, uses a file lock on Windows rather than an actual mutex. If your application terminates abnormally, that file lock will not be removed from the system. This is actually subject to an open issue.
Looking at the source code, we see that, if BOOST_INTERPROCESS_USE_WINDOWS is defined, internal_mutex_type maps to a windows_named_mutex which, internally, uses a windows_named_sync, which seems to just be using a file lock in the end. I'm not sure what exactly is the rationale of this choice of implementation. Whatever it may be, there does not seem to be any way to get boost::interprocess to use a proper named mutex on Windows. I would suggest to simply create a named mutex yourself using CreateMutex, for example:
#include <type_traits>
#include <memory>
#include <stdexcept>
#include <mutex>
#include <iostream>
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct CloseHandleDeleter { void operator ()(HANDLE h) const { CloseHandle(h); } };
class NamedMutex
{
std::unique_ptr<std::remove_pointer_t<HANDLE>, CloseHandleDeleter> m;
public:
NamedMutex(const wchar_t* name)
: m(CreateMutexW(nullptr, FALSE, name))
{
if (!m)
throw std::runtime_error("failed to create mutex");
}
void lock()
{
if (WaitForSingleObject(m.get(), INFINITE) == WAIT_FAILED)
throw std::runtime_error("something bad happened");
}
void unlock()
{
ReleaseMutex(m.get());
}
};
int main()
{
try
{
NamedMutex mutex(L"blub");
std::lock_guard lock(mutex);
std::cout << "Hello, World!" << std::endl;
}
catch (...)
{
std::cerr << "something went wrong\n";
return -1;
}
return 0;
}
Can someone please explain what is causing this behavior?
The mutex is global.
How can I reset the behavior for this particular mutex?
Call boost::interprocess::named_mutex::remove("mutex_name");
Most importantly, how to avoid this scenario in a real application?
It depends on what your outer problem is. Perhaps a more sensible solution is to use a file lock instead. A file lock will go away when a process is destroyed.
Updates:
I understand mutex is global but what happens with that mutex that causes the program to hang?
The first program acquired the mutex and never released it so the mutex is still held. Mutexes are typically held while shared state is put into an inconsistent state, so automatically releasing the mutex would be disastrous.
How can I determine if that mutex_name is in a bad state so its time to call the remove on it?
In your case you really can't because you picked the wrong tool for the job. The same logic you would use to tell if the mutex was in a sane state would just solve your whole problem, so the mutex just made things harder. Instead, use a file lock. It may be useful to write the process name and process ID into the file to help in troubleshooting.

Asynchronously writing to a file in c++ unix

I have some long loop that I need to write some data to a file on every iteration. The problem is that writing to a file can be slow, so I would like to reduce the time this takes by doing the writing asynchronously.
Does anyone know a good way to do this? Should I be creating a thread that consumes whatever is put into it's buffer by writing it out ( in this case, a single producer, single consumer )?
I am interested mostly in solutions that don't involve anything but the standard library (C++11).
Before going into asynchronous writing, if you are using IOStreams you might want to try to avoid flushing the stream accidentally, e.g., by not using std::endl but rather using '\n' instead. Since writing to IOStreams is buffered this can improve performance quite a bit.
If that's not sufficient, the next question is how the data is written. If there is a lot of formatting going on, there is a chance that the actual formatting takes most of the time. You might be able to push the formatting off into a separate thread but that's quite different from merely passing off writing a couple of bytes to another thread: you'd need to pass on a suitable data structure holding the data to be formatted. What is suitable depends on what you are actually writing, though.
Finally, if writing the buffers to a file is really the bottleneck and you want to stick with the standard C++ library, it may be reasonable to have a writer thread which listens on a queue filled with buffers from a suitable stream buffer and writes the buffers to an std::ofstream: the producer interface would be an std::ostream which would send off probably fixed sized buffers either when the buffer is full or when the stream is flushed (for which I'd use std::flush explicitly) to a queue on which the other read listens. Below is a quick implementation of that idea using only standard library facilities:
#include <condition_variable>
#include <fstream>
#include <mutex>
#include <queue>
#include <streambuf>
#include <string>
#include <thread>
#include <vector>
struct async_buf
: std::streambuf
{
std::ofstream out;
std::mutex mutex;
std::condition_variable condition;
std::queue<std::vector<char>> queue;
std::vector<char> buffer;
bool done;
std::thread thread;
void worker() {
bool local_done(false);
std::vector<char> buf;
while (!local_done) {
{
std::unique_lock<std::mutex> guard(this->mutex);
this->condition.wait(guard,
[this](){ return !this->queue.empty()
|| this->done; });
if (!this->queue.empty()) {
buf.swap(queue.front());
queue.pop();
}
local_done = this->queue.empty() && this->done;
}
if (!buf.empty()) {
out.write(buf.data(), std::streamsize(buf.size()));
buf.clear();
}
}
out.flush();
}
public:
async_buf(std::string const& name)
: out(name)
, buffer(128)
, done(false)
, thread(&async_buf::worker, this) {
this->setp(this->buffer.data(),
this->buffer.data() + this->buffer.size() - 1);
}
~async_buf() {
std::unique_lock<std::mutex>(this->mutex), (this->done = true);
this->condition.notify_one();
this->thread.join();
}
int overflow(int c) {
if (c != std::char_traits<char>::eof()) {
*this->pptr() = std::char_traits<char>::to_char_type(c);
this->pbump(1);
}
return this->sync() != -1
? std::char_traits<char>::not_eof(c): std::char_traits<char>::eof();
}
int sync() {
if (this->pbase() != this->pptr()) {
this->buffer.resize(std::size_t(this->pptr() - this->pbase()));
{
std::unique_lock<std::mutex> guard(this->mutex);
this->queue.push(std::move(this->buffer));
}
this->condition.notify_one();
this->buffer = std::vector<char>(128);
this->setp(this->buffer.data(),
this->buffer.data() + this->buffer.size() - 1);
}
return 0;
}
};
int main()
{
async_buf sbuf("async.out");
std::ostream astream(&sbuf);
std::ifstream in("async_stream.cpp");
for (std::string line; std::getline(in, line); ) {
astream << line << '\n' << std::flush;
}
}
Search the web for "double buffering."
In general, one thread will write to one or more buffers. Another thread reads from the buffers, "chasing" the writing thread.
This may not make your program more efficient. Efficiency with files is achieved by writing in huge blocks so that the drive doesn't get a chance to spin down. One write of many bytes is more efficient than many writes of a few bytes.
This could be achieved by having the writing thread only write when the buffer content has exceeded some threshold like 1k.
Also research the topic of "spooling" or "print spooling".
You'll need to use C++11 since previous versions don't have threading support in the standard library. I don't know why you limit yourself, since Boost has some good stuff in it.

Multiple DLLs writing to the same text file?

I want to add logging support to a COM object (DLL) of mine, and there are usually at least two instances of this object loaded. I want both of the DLLs to be able to write lines to the same text file but am worried that this is going to give me problems. Is this possible to achieve? Am I right in thinking that a single Windows API WriteFile call is atomic? Can two processes both open the same file for writing?
I'd like to use the STL for the file handling here (std::ofstream) if possible. My other idea is to use a separate log per-DLL but a single log would be much easier to manage.
#include <iostream>
#include <fstream>
#include <windosws.h>
struct Mutex {
Mutex () {
h = ::CreateMutex(0, false, "{any-GUID-1247965802375274724957}");
}
~Mutex () {
::CloseHandle(h);
}
HANDLE h;
};
Mutex mutex; // GLOBAL mutex
void dll_write_func() {
::WaitForSingleObject(mutex.h, INFINITE);
////////////////
// Write here
std::ofstrem f("output.txt");
f << "Test" << std::endl;
////////////////
::ReleaseMutex(mutex.h);
}
Or
struct MutexLock {
explicit MutexLock(Mutex & m) : m(m) {
::WaitForSingleObject(m.h, INFINITE);
}
~MutexLock() {
::ReleaseMutex(m.h);
}
Mutex & m;
};
void dll_write_func2() {
MutexLock mlock(mutex);
// Write here
std::ofstrem f("output.txt");
f << "Auto mutex Release" << std::endl;
}