1313
1414module Concore
1515
16+ using Mmap
17+
1618# -----------------------------------------------------------------------------
1719# Backend selection
1820# -----------------------------------------------------------------------------
@@ -23,8 +25,19 @@ abstract type AbstractBackend end
2325""" Default local file transport backend."""
2426struct FileBackend <: AbstractBackend end
2527
28+ """ Memory-mapped local file transport backend."""
29+ struct MmapBackend <: AbstractBackend
30+ segment_size:: Int
31+
32+ function MmapBackend (segment_size:: Int = 4096 )
33+ segment_size > 1 || throw (ArgumentError (" mmap segment size must be greater than 1" ))
34+ new (segment_size)
35+ end
36+ end
37+
2638# Compatibility name for proposal/docs wording.
2739const FileTransport = FileBackend
40+ const MmapTransport = MmapBackend
2841
2942# -----------------------------------------------------------------------------
3043# Path configuration
@@ -82,10 +95,146 @@ const _S_MAX_LEN = 65_536
8295
8396_backend_inpath (:: FileBackend ) = inpath
8497_backend_outpath (:: FileBackend ) = outpath
98+ _backend_inpath (:: MmapBackend ) = inpath
99+ _backend_outpath (:: MmapBackend ) = outpath
85100
86101_input_dir (port:: Int ) = _backend_inpath (_backend) * string (port)
87102_output_dir (port:: Int ) = _backend_outpath (_backend) * string (port)
88103
104+ const _mmap_segments = Dict {String, Tuple{IOStream, Vector{UInt8}}} ()
105+ const _mmap_cleanup_registered = Ref (false )
106+
107+ function _register_mmap_cleanup ()
108+ if ! _mmap_cleanup_registered[]
109+ atexit (mmap_cleanup)
110+ _mmap_cleanup_registered[] = true
111+ end
112+ end
113+
114+ function _close_mmap_segment (path:: AbstractString )
115+ segment = pop! (_mmap_segments, String (path), nothing )
116+ segment === nothing && return nothing
117+
118+ io, buf = segment
119+ try
120+ Mmap. sync! (buf)
121+ catch
122+ end
123+ try
124+ finalize (buf)
125+ catch
126+ end
127+ try
128+ isopen (io) && close (io)
129+ catch
130+ end
131+ GC. gc ()
132+ return nothing
133+ end
134+
135+ function _mmap_segment (path:: AbstractString , size:: Int ):: Vector{UInt8}
136+ segment = get (_mmap_segments, String (path), nothing )
137+ if segment != = nothing && isopen (segment[1 ]) && length (segment[2 ]) >= size
138+ return segment[2 ]
139+ end
140+ if segment != = nothing
141+ _close_mmap_segment (path)
142+ end
143+
144+ _register_mmap_cleanup ()
145+ mkpath (dirname (path))
146+
147+ io = open (path, isfile (path) ? " r+" : " w+" )
148+ if filesize (path) < size
149+ seek (io, size - 1 )
150+ write (io, UInt8 (0 ))
151+ seekstart (io)
152+ end
153+
154+ buf = try
155+ Mmap. mmap (io, Vector{UInt8}, size)
156+ catch
157+ close (io)
158+ rethrow ()
159+ end
160+ _mmap_segments[String (path)] = (io, buf)
161+ return buf
162+ end
163+
164+ function _mmap_content (buf:: Vector{UInt8} ):: String
165+ nullpos = findfirst (iszero, buf)
166+ last = nullpos === nothing ? length (buf) : nullpos - 1
167+ last <= 0 && return " "
168+ return String (copy (buf[1 : last]))
169+ end
170+
171+ function _mmap_read (path:: AbstractString , initstr:: AbstractString , size:: Int ):: String
172+ isfile (path) || return String (initstr)
173+ buf = _mmap_segment (path, size)
174+ ins = _mmap_content (buf)
175+ return isempty (ins) ? String (initstr) : ins
176+ end
177+
178+ function _mmap_write (path:: AbstractString , wire:: AbstractString , size:: Int )
179+ bytes = Vector {UInt8} (wire)
180+ if length (bytes) >= size
181+ throw (ArgumentError (" mmap payload exceeds segment size" ))
182+ end
183+
184+ buf = _mmap_segment (path, size)
185+ n = length (bytes)
186+ buf[1 : n] .= bytes
187+ buf[n + 1 ] = 0x00
188+ if n + 2 <= size
189+ buf[n + 2 : size] .= 0x00
190+ end
191+ Mmap. sync! (buf)
192+ return nothing
193+ end
194+
195+ function _write_wire_file (path:: AbstractString , wire:: AbstractString )
196+ segment = get (_mmap_segments, String (path), nothing )
197+ if segment != = nothing && isopen (segment[1 ])
198+ return _mmap_write (path, wire, length (segment[2 ]))
199+ end
200+
201+ open (path, " w" ) do f
202+ write (f, wire)
203+ end
204+ return nothing
205+ end
206+
207+ """ Close open memory-mapped segment handles."""
208+ function mmap_cleanup ()
209+ segments = collect (values (_mmap_segments))
210+ empty! (_mmap_segments)
211+
212+ for (io, buf) in segments
213+ try
214+ Mmap. sync! (buf)
215+ catch
216+ end
217+ try
218+ finalize (buf)
219+ catch
220+ end
221+ try
222+ isopen (io) && close (io)
223+ catch
224+ end
225+ end
226+
227+ segments = nothing
228+ GC. gc ()
229+ return nothing
230+ end
231+
232+ function _wire_content (raw:: AbstractString ):: String
233+ nullpos = findfirst (== (' \0 ' ), raw)
234+ nullpos === nothing && return String (raw)
235+ return String (raw[1 : nullpos - 1 ])
236+ end
237+
89238function _port_file_path (base:: AbstractString , port:: Int , name:: AbstractString ):: String
90239 port_dir = abspath (base * string (port))
91240 filepath = abspath (joinpath (port_dir, String (name)))
@@ -312,7 +461,11 @@ function concore_read(
312461
313462 ins = " "
314463 try
315- ins = read (filepath, String)
464+ if _backend isa MmapBackend
465+ ins = _mmap_read (filepath, initstr, _backend. segment_size)
466+ else
467+ ins = _wire_content (read (filepath, String))
468+ end
316469 catch
317470 ins = initstr
318471 end
@@ -322,7 +475,11 @@ function concore_read(
322475 while isempty (ins) && attempts < 5
323476 sleep (delay)
324477 try
325- ins = read (filepath, String)
478+ if _backend isa MmapBackend
479+ ins = _mmap_read (filepath, initstr, _backend. segment_size)
480+ else
481+ ins = _wire_content (read (filepath, String))
482+ end
326483 catch
327484 end
328485 attempts += 1
@@ -359,8 +516,10 @@ function concore_write(
359516 outval = Float64[simtime + Float64 (delta); Float64 .(val)]
360517 wire = _format_wire (outval)
361518
362- open (filepath, " w" ) do f
363- write (f, wire)
519+ if _backend isa MmapBackend
520+ _mmap_write (filepath, wire, _backend. segment_size)
521+ else
522+ _write_wire_file (filepath, wire)
364523 end
365524
366525 return nothing
@@ -380,8 +539,10 @@ function concore_write(
380539 sleep (2 * delay)
381540 filepath = _port_file_path (_backend_outpath (_backend), port, name)
382541 mkpath (dirname (filepath))
383- open (filepath, " w" ) do f
384- write (f, val)
542+ if _backend isa MmapBackend
543+ _mmap_write (filepath, val, _backend. segment_size)
544+ else
545+ _write_wire_file (filepath, val)
385546 end
386547 return nothing
387548end
@@ -432,6 +593,13 @@ function concore_init!()
432593 return nothing
433594end
434595
596+ function concore_init! (backend:: AbstractBackend )
597+ global _backend
598+ _backend = backend
599+ concore_init! ()
600+ return nothing
601+ end
602+
435603# Backward-compatible aliases (without !)
436604const load_iport = load_iport!
437605const load_oport = load_oport!
@@ -448,6 +616,7 @@ export tryparam, default_maxtime!, safe_parse_list
448616export load_iport!, load_oport!, load_params!, concore_init!
449617export load_iport, load_oport, load_params, default_maxtime, concore_init
450618export AbstractBackend, FileBackend, FileTransport
619+ export MmapBackend, MmapTransport, mmap_cleanup
451620
452621# -----------------------------------------------------------------------------
453622# Auto-initialize on load
0 commit comments