@@ -203,15 +203,18 @@ end
203
203
mutable struct SendQueue
204
204
queue:: Channel{Any}
205
205
@atomic task:: Union{Task,Nothing}
206
+ processing:: Bool
206
207
end
207
- const SEND_QUEUE = SendQueue (Channel (typemax (Int)), nothing )
208
+ const SEND_QUEUE = SendQueue (Channel (typemax (Int)), nothing , false )
208
209
function _enqueue_work (f, args... ; gc_context= false )
209
210
if SEND_QUEUE. task === nothing
210
211
task = Task () do
211
212
while true
212
213
try
213
214
work, _args = take! (SEND_QUEUE. queue)
215
+ SEND_QUEUE. processing = true
214
216
work (_args... )
217
+ SEND_QUEUE. processing = false
215
218
catch err
216
219
exit_flag[] && continue
217
220
err isa ProcessExitedException && continue # TODO : Remove proc from counters
@@ -348,12 +351,73 @@ isondisk(id::Int) =
348
351
isinmemory (x:: DRef ) = isinmemory (x. id)
349
352
isondisk (x:: DRef ) = isondisk (x. id)
350
353
354
+ const MEM_RESERVED = Ref {UInt} (512 * (1024 ^ 2 )) # Reserve 512MB of RAM for OS
355
+ const MEM_RESERVE_LOCK = Threads. ReentrantLock ()
356
+
357
+ """
358
+ When called, ensures that at least `MEM_RESERVED[] + size` bytes are available
359
+ to the OS. If there is not enough memory available, then a variety of calls to
360
+ the GC are performed to free up memory until either the reservation limit is
361
+ satisfied, or `max_sweeps` number of cycles have elapsed.
362
+ """
363
+ function ensure_memory_reserved (size:: Integer = 0 ; max_sweeps:: Integer = 5 )
364
+ sat_sub (x:: T , y:: T ) where T = x < y ? zero (T) : x- y
365
+
366
+ # Check whether the OS is running tight on memory
367
+ sweep_ctr = 0
368
+ while true
369
+ with (QUERY_MEM_OVERRIDE => true ) do
370
+ Int (storage_available (CPURAMResource ())) - size < MEM_RESERVED[]
371
+ end || break
372
+
373
+ # We need more memory! Let's encourage the GC to clear some memory...
374
+ sweep_start = time_ns ()
375
+ mem_used = with (QUERY_MEM_OVERRIDE => true ) do
376
+ storage_utilized (CPURAMResource ())
377
+ end
378
+ if sweep_ctr == 0
379
+ @debug " Not enough memory to continue! Sweeping up unused memory..."
380
+ GC. gc (false )
381
+ elseif sweep_ctr == 1
382
+ GC. gc (true )
383
+ else
384
+ @everywhere GC. gc (true )
385
+ end
386
+
387
+ # Let finalizers run
388
+ yield ()
389
+
390
+ # Wait for send queue to clear
391
+ while SEND_QUEUE. processing
392
+ yield ()
393
+ end
394
+
395
+ with (QUERY_MEM_OVERRIDE => true ) do
396
+ mem_freed = sat_sub (mem_used, storage_utilized (CPURAMResource ()))
397
+ @debug " Freed $(Base. format_bytes (mem_freed)) bytes, available: $(Base. format_bytes (storage_available (CPURAMResource ()))) "
398
+ end
399
+
400
+ sweep_ctr += 1
401
+ if sweep_ctr == max_sweeps
402
+ @debug " Made too many sweeps, bailing out..."
403
+ break
404
+ end
405
+ end
406
+ if sweep_ctr > 0
407
+ @debug " Swept for $sweep_ctr cycles"
408
+ end
409
+ end
410
+
351
411
function poolset (@nospecialize (x), pid= myid (); size= approx_size (x),
352
412
retain= false , restore= false ,
353
413
device= GLOBAL_DEVICE[], leaf_device= initial_leaf_device (device),
354
414
tag= nothing , leaf_tag= Tag (),
355
415
destructor= nothing )
356
416
if pid == myid ()
417
+ if ! restore
418
+ @lock MEM_RESERVE_LOCK ensure_memory_reserved (size)
419
+ end
420
+
357
421
id = atomic_add! (id_counter, 1 )
358
422
sstate = if ! restore
359
423
StorageState (Some {Any} (x),
0 commit comments