@@ -228,6 +228,37 @@ impl ManuallyRooted<ArrayRef> {
228
228
}
229
229
}
230
230
231
+ /// An iterator for elements in `ArrayRef::new[_async].
232
+ ///
233
+ /// NB: We can't use `iter::repeat(elem).take(len)` because that doesn't
234
+ /// implement `ExactSizeIterator`.
235
+ #[ derive( Clone ) ]
236
+ struct RepeatN < ' a > ( & ' a Val , u32 ) ;
237
+
238
+ impl < ' a > Iterator for RepeatN < ' a > {
239
+ type Item = & ' a Val ;
240
+
241
+ fn next ( & mut self ) -> Option < Self :: Item > {
242
+ if self . 1 == 0 {
243
+ None
244
+ } else {
245
+ self . 1 -= 1 ;
246
+ Some ( self . 0 )
247
+ }
248
+ }
249
+
250
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
251
+ let len = self . len ( ) ;
252
+ ( len, Some ( len) )
253
+ }
254
+ }
255
+
256
+ impl ExactSizeIterator for RepeatN < ' _ > {
257
+ fn len ( & self ) -> usize {
258
+ usize:: try_from ( self . 1 ) . unwrap ( )
259
+ }
260
+ }
261
+
231
262
impl ArrayRef {
232
263
/// Allocate a new `array` of the given length, with every element
233
264
/// initialized to `elem`.
@@ -237,18 +268,28 @@ impl ArrayRef {
237
268
///
238
269
/// This is similar to the `array.new` instruction.
239
270
///
271
+ /// # Automatic Garbage Collection
272
+ ///
273
+ /// If the GC heap is at capacity, and there isn't room for allocating this
274
+ /// new array, then this method will automatically trigger a synchronous
275
+ /// collection in an attempt to free up space in the GC heap.
276
+ ///
240
277
/// # Errors
241
278
///
242
279
/// If the given `elem` value's type does not match the `allocator`'s array
243
280
/// type's element type, an error is returned.
244
281
///
245
282
/// If the allocation cannot be satisfied because the GC heap is currently
246
- /// out of memory, but performing a garbage collection might free up space
247
- /// such that retrying the allocation afterwards might succeed, then a
248
- /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned .
283
+ /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
284
+ /// error is returned. The allocation might succeed on a second attempt if
285
+ /// you drop some rooted GC references and try again .
249
286
///
250
287
/// # Panics
251
288
///
289
+ /// Panics if the `store` is configured for async; use
290
+ /// [`ArrayRef::new_async`][crate::ArrayRef::new_async] to perform
291
+ /// asynchronous allocation instead.
292
+ ///
252
293
/// Panics if either the allocator or the `elem` value is not associated
253
294
/// with the given store.
254
295
pub fn new (
@@ -266,54 +307,106 @@ impl ArrayRef {
266
307
elem : & Val ,
267
308
len : u32 ,
268
309
) -> Result < Rooted < ArrayRef > > {
269
- assert_eq ! (
270
- store. id( ) ,
271
- allocator. store_id,
272
- "attempted to use a `ArrayRefPre` with the wrong store"
273
- ) ;
310
+ store. retry_after_gc ( ( ) , |store, ( ) | {
311
+ Self :: new_from_iter ( store, allocator, RepeatN ( elem, len) )
312
+ } )
313
+ }
314
+
315
+ /// Asynchronously allocate a new `array` of the given length, with every
316
+ /// element initialized to `elem`.
317
+ ///
318
+ /// For example, `ArrayRef::new(ctx, pre, &Val::I64(9), 3)` allocates the
319
+ /// array `[9, 9, 9]`.
320
+ ///
321
+ /// This is similar to the `array.new` instruction.
322
+ ///
323
+ /// # Automatic Garbage Collection
324
+ ///
325
+ /// If the GC heap is at capacity, and there isn't room for allocating this
326
+ /// new array, then this method will automatically trigger a asynchronous
327
+ /// collection in an attempt to free up space in the GC heap.
328
+ ///
329
+ /// # Errors
330
+ ///
331
+ /// If the given `elem` value's type does not match the `allocator`'s array
332
+ /// type's element type, an error is returned.
333
+ ///
334
+ /// If the allocation cannot be satisfied because the GC heap is currently
335
+ /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
336
+ /// error is returned. The allocation might succeed on a second attempt if
337
+ /// you drop some rooted GC references and try again.
338
+ ///
339
+ /// # Panics
340
+ ///
341
+ /// Panics if your engine is not configured for async; use
342
+ /// [`ArrayRef::new_async`][crate::ArrayRef::new_async] to perform
343
+ /// synchronous allocation instead.
344
+ ///
345
+ /// Panics if either the allocator or the `elem` value is not associated
346
+ /// with the given store.
347
+ #[ cfg( feature = "async" ) ]
348
+ pub async fn new_async (
349
+ mut store : impl AsContextMut ,
350
+ allocator : & ArrayRefPre ,
351
+ elem : & Val ,
352
+ len : u32 ,
353
+ ) -> Result < Rooted < ArrayRef > > {
354
+ Self :: _new_async ( store. as_context_mut ( ) . 0 , allocator, elem, len) . await
355
+ }
356
+
357
+ #[ cfg( feature = "async" ) ]
358
+ pub ( crate ) async fn _new_async (
359
+ store : & mut StoreOpaque ,
360
+ allocator : & ArrayRefPre ,
361
+ elem : & Val ,
362
+ len : u32 ,
363
+ ) -> Result < Rooted < ArrayRef > > {
364
+ store
365
+ . retry_after_gc_async ( ( ) , |store, ( ) | {
366
+ Self :: new_from_iter ( store, allocator, RepeatN ( elem, len) )
367
+ } )
368
+ . await
369
+ }
274
370
371
+ /// Like `ArrayRef::new` but when async is configured must only ever be
372
+ /// called from on a fiber stack.
373
+ pub ( crate ) unsafe fn new_maybe_async (
374
+ store : & mut StoreOpaque ,
375
+ allocator : & ArrayRefPre ,
376
+ elem : & Val ,
377
+ len : u32 ,
378
+ ) -> Result < Rooted < ArrayRef > > {
275
379
// Type check the initial element value against the element type.
276
380
elem. ensure_matches_ty ( store, allocator. ty . element_type ( ) . unpack ( ) )
277
381
. context ( "element type mismatch" ) ?;
278
382
279
- return Self :: _new_unchecked ( store, allocator, RepeatN ( elem, len) ) ;
280
-
281
- // NB: Can't use `iter::repeat(elem).take(len)` above because that
282
- // doesn't implement `ExactSizeIterator`.
283
- struct RepeatN < ' a > ( & ' a Val , u32 ) ;
284
-
285
- impl < ' a > Iterator for RepeatN < ' a > {
286
- type Item = & ' a Val ;
287
-
288
- fn next ( & mut self ) -> Option < Self :: Item > {
289
- if self . 1 == 0 {
290
- None
291
- } else {
292
- self . 1 -= 1 ;
293
- Some ( self . 0 )
294
- }
295
- }
296
-
297
- fn size_hint ( & self ) -> ( usize , Option < usize > ) {
298
- let len = self . len ( ) ;
299
- ( len, Some ( len) )
300
- }
301
- }
302
-
303
- impl ExactSizeIterator for RepeatN < ' _ > {
304
- fn len ( & self ) -> usize {
305
- usize:: try_from ( self . 1 ) . unwrap ( )
306
- }
383
+ unsafe {
384
+ store. retry_after_gc_maybe_async ( ( ) , |store, ( ) | {
385
+ Self :: new_from_iter ( store, allocator, RepeatN ( elem, len) )
386
+ } )
307
387
}
308
388
}
309
389
310
- /// Allocate a new array of the given elements, without checking that the
311
- /// elements' types match the array's element type.
312
- fn _new_unchecked < ' a > (
390
+ /// Allocate a new array of the given elements.
391
+ ///
392
+ /// Does not attempt a GC on OOM; leaves that to callers.
393
+ fn new_from_iter < ' a > (
313
394
store : & mut StoreOpaque ,
314
395
allocator : & ArrayRefPre ,
315
- elems : impl ExactSizeIterator < Item = & ' a Val > ,
396
+ elems : impl Clone + ExactSizeIterator < Item = & ' a Val > ,
316
397
) -> Result < Rooted < ArrayRef > > {
398
+ assert_eq ! (
399
+ store. id( ) ,
400
+ allocator. store_id,
401
+ "attempted to use a `ArrayRefPre` with the wrong store"
402
+ ) ;
403
+
404
+ // Type check the elements against the element type.
405
+ for elem in elems. clone ( ) {
406
+ elem. ensure_matches_ty ( store, allocator. ty . element_type ( ) . unpack ( ) )
407
+ . context ( "element type mismatch" ) ?;
408
+ }
409
+
317
410
let len = u32:: try_from ( elems. len ( ) ) . unwrap ( ) ;
318
411
319
412
// Allocate the array and write each field value into the appropriate
@@ -346,25 +439,35 @@ impl ArrayRef {
346
439
}
347
440
}
348
441
349
- /// Allocate a new `array` containing the given elements.
442
+ /// Synchronously allocate a new `array` containing the given elements.
350
443
///
351
444
/// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5),
352
445
/// Val::I64(6)])` allocates the array `[4, 5, 6]`.
353
446
///
354
447
/// This is similar to the `array.new_fixed` instruction.
355
448
///
449
+ /// # Automatic Garbage Collection
450
+ ///
451
+ /// If the GC heap is at capacity, and there isn't room for allocating this
452
+ /// new array, then this method will automatically trigger a synchronous
453
+ /// collection in an attempt to free up space in the GC heap.
454
+ ///
356
455
/// # Errors
357
456
///
358
457
/// If any of the `elems` values' type does not match the `allocator`'s
359
458
/// array type's element type, an error is returned.
360
459
///
361
460
/// If the allocation cannot be satisfied because the GC heap is currently
362
- /// out of memory, but performing a garbage collection might free up space
363
- /// such that retrying the allocation afterwards might succeed, then a
364
- /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned .
461
+ /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
462
+ /// error is returned. The allocation might succeed on a second attempt if
463
+ /// you drop some rooted GC references and try again .
365
464
///
366
465
/// # Panics
367
466
///
467
+ /// Panics if the `store` is configured for async; use
468
+ /// [`ArrayRef::new_fixed_async`][crate::ArrayRef::new_fixed_async] to
469
+ /// perform asynchronous allocation instead.
470
+ ///
368
471
/// Panics if the allocator or any of the `elems` values are not associated
369
472
/// with the given store.
370
473
pub fn new_fixed (
@@ -380,19 +483,81 @@ impl ArrayRef {
380
483
allocator : & ArrayRefPre ,
381
484
elems : & [ Val ] ,
382
485
) -> Result < Rooted < ArrayRef > > {
383
- assert_eq ! (
384
- store. id( ) ,
385
- allocator. store_id,
386
- "attempted to use a `ArrayRefPre` with the wrong store"
387
- ) ;
486
+ store. retry_after_gc ( ( ) , |store, ( ) | {
487
+ Self :: new_from_iter ( store, allocator, elems. iter ( ) )
488
+ } )
489
+ }
388
490
389
- // Type check the elements against the element type.
390
- for elem in elems {
391
- elem. ensure_matches_ty ( store, allocator. ty . element_type ( ) . unpack ( ) )
392
- . context ( "element type mismatch" ) ?;
393
- }
491
+ /// Asynchronously allocate a new `array` containing the given elements.
492
+ ///
493
+ /// For example, `ArrayRef::new_fixed_async(ctx, pre, &[Val::I64(4),
494
+ /// Val::I64(5), Val::I64(6)])` allocates the array `[4, 5, 6]`.
495
+ ///
496
+ /// This is similar to the `array.new_fixed` instruction.
497
+ ///
498
+ /// If your engine is not configured for async, use
499
+ /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
500
+ /// synchronous allocation.
501
+ ///
502
+ /// # Automatic Garbage Collection
503
+ ///
504
+ /// If the GC heap is at capacity, and there isn't room for allocating this
505
+ /// new array, then this method will automatically trigger a synchronous
506
+ /// collection in an attempt to free up space in the GC heap.
507
+ ///
508
+ /// # Errors
509
+ ///
510
+ /// If any of the `elems` values' type does not match the `allocator`'s
511
+ /// array type's element type, an error is returned.
512
+ ///
513
+ /// If the allocation cannot be satisfied because the GC heap is currently
514
+ /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
515
+ /// error is returned. The allocation might succeed on a second attempt if
516
+ /// you drop some rooted GC references and try again.
517
+ ///
518
+ /// # Panics
519
+ ///
520
+ /// Panics if the `store` is not configured for async; use
521
+ /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
522
+ /// synchronous allocation instead.
523
+ ///
524
+ /// Panics if the allocator or any of the `elems` values are not associated
525
+ /// with the given store.
526
+ #[ cfg( feature = "async" ) ]
527
+ pub async fn new_fixed_async (
528
+ mut store : impl AsContextMut ,
529
+ allocator : & ArrayRefPre ,
530
+ elems : & [ Val ] ,
531
+ ) -> Result < Rooted < ArrayRef > > {
532
+ Self :: _new_fixed_async ( store. as_context_mut ( ) . 0 , allocator, elems) . await
533
+ }
394
534
395
- return Self :: _new_unchecked ( store, allocator, elems. iter ( ) ) ;
535
+ #[ cfg( feature = "async" ) ]
536
+ pub ( crate ) async fn _new_fixed_async (
537
+ store : & mut StoreOpaque ,
538
+ allocator : & ArrayRefPre ,
539
+ elems : & [ Val ] ,
540
+ ) -> Result < Rooted < ArrayRef > > {
541
+ store
542
+ . retry_after_gc_async ( ( ) , |store, ( ) | {
543
+ Self :: new_from_iter ( store, allocator, elems. iter ( ) )
544
+ } )
545
+ . await
546
+ }
547
+
548
+ /// Like `ArrayRef::new_fixed[_async]` but it is the caller's responsibility
549
+ /// to ensure that when async is enabled, this is only called from on a
550
+ /// fiber stack.
551
+ pub ( crate ) unsafe fn new_fixed_maybe_async (
552
+ store : & mut StoreOpaque ,
553
+ allocator : & ArrayRefPre ,
554
+ elems : & [ Val ] ,
555
+ ) -> Result < Rooted < ArrayRef > > {
556
+ unsafe {
557
+ store. retry_after_gc_maybe_async ( ( ) , |store, ( ) | {
558
+ Self :: new_from_iter ( store, allocator, elems. iter ( ) )
559
+ } )
560
+ }
396
561
}
397
562
398
563
#[ inline]
@@ -478,7 +643,7 @@ impl ArrayRef {
478
643
479
644
/// Get the values of this array's elements.
480
645
///
481
- /// Note that `i8` and `i16` field values are zero-extended into
646
+ /// Note that `i8` and `i16` element values are zero-extended into
482
647
/// `Val::I32(_)`s.
483
648
///
484
649
/// # Errors
0 commit comments