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 /// `Memory` implementation encapsulating a memory-mapped file.
31 /// When the last reference is released, the file is unmapped.
32 class MappedMemory : Memory
33 {
34 	typeof(scoped!MmFile(null)) mmFile; /// The `MmFile` object.
35 	ubyte[] 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 = cast(ubyte[])(
43 			(from || to)
44 			? mmFile.Scoped_payload[from..(to ? to : mmFile.length)]
45 			: mmFile.Scoped_payload[]
46 		);
47 
48 		debug(DATA_REFCOUNT) writefln("? -> %s [%s..%s]: Created MappedMemory", cast(void*)this, contents.ptr, contents.ptr + contents.length);
49 	} ///
50 
51 	debug(DATA_REFCOUNT)
52 	~this() @nogc
53 	{
54 		printf("? -> %p: Deleted MappedMemory\n", cast(void*)this);
55 	}
56 
57 	override @property inout(ubyte)[] contents() inout { return mappedData; } ///
58 	override @property size_t size() const { return mappedData.length; } ///
59 	override void setSize(size_t newSize) { assert(false, "Can't resize MappedMemory"); } ///
60 	override @property size_t capacity() const { return mappedData.length; } ///
61 }
62 
63 /// Returns a `Data` viewing a mapped file.
64 Data mapFile(string name, MmMode mode, size_t from = 0, size_t to = 0)
65 {
66 	auto memory = unmanagedNew!MappedMemory(name, mode, from, to);
67 	return Data(memory);
68 }
69 
70 private T retryInterrupted(T)(scope T delegate() dg)
71 {
72 	version (Posix)
73 	{
74 		while (true)
75 		{
76 			try
77 			{
78 				return dg();
79 			}
80 			catch (ErrnoException e)
81 			{
82 				if (e.errno == EINTR)
83 					continue;
84 				throw e;
85 			}
86 		}
87 	}
88 	else
89 		return dg();
90 }