1 /**
2  * ae.sys.datamm
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.sys.datamm;
15 
16 import core.stdc.errno;
17 
18 import std.exception;
19 import std.mmfile;
20 import std.typecons;
21 
22 debug(DATA_REFCOUNT) import std.stdio, core.stdc.stdio;
23 
24 import ae.sys.data;
25 
26 alias MmMode = MmFile.Mode; /// Convenience alias.
27 
28 // ************************************************************************
29 
30 /// `DataWrapper` implementation encapsulating a memory-mapped file.
31 /// When the last reference is released, the file is unmapped.
32 class MappedDataWrapper : DataWrapper
33 {
34 	typeof(scoped!MmFile(null)) mmFile; /// The `MmFile` object.
35 	void[] mappedData; /// View of the mapped file data.
36 
37 	this(string name, MmMode mode, size_t from, size_t to)
38 	{
39 		mmFile = retryInterrupted({
40 			return scoped!MmFile(name, mode, 0, null);
41 		});
42 		mappedData = (from || to) ? mmFile.Scoped_payload[from..(to ? to : mmFile.length)] : mmFile.Scoped_payload[];
43 
44 		debug(DATA_REFCOUNT) writefln("? -> %s [%s..%s]: Created MappedDataWrapper", cast(void*)this, contents.ptr, contents.ptr + contents.length);
45 	} ///
46 
47 	debug(DATA_REFCOUNT)
48 	~this() @nogc
49 	{
50 		printf("? -> %p: Deleted MappedDataWrapper\n", cast(void*)this);
51 	}
52 
53 	override @property inout(void)[] contents() inout { return mappedData; } ///
54 	override @property size_t size() const { return mappedData.length; } ///
55 	override void setSize(size_t newSize) { assert(false, "Can't resize MappedDataWrapper"); } ///
56 	override @property size_t capacity() const { return mappedData.length; } ///
57 }
58 
59 /// Returns a `Data` viewing a mapped file.
60 Data mapFile(string name, MmMode mode, size_t from = 0, size_t to = 0)
61 {
62 	auto wrapper = unmanagedNew!MappedDataWrapper(name, mode, from, to);
63 	return Data(wrapper, mode != MmMode.read);
64 }
65 
66 private T retryInterrupted(T)(scope T delegate() dg)
67 {
68 	version (Posix)
69 	{
70 		while (true)
71 		{
72 			try
73 			{
74 				return dg();
75 			}
76 			catch (ErrnoException e)
77 			{
78 				if (e.errno == EINTR)
79 					continue;
80 				throw e;
81 			}
82 		}
83 	}
84 	else
85 		return dg();
86 }