Skip to content

Commit a294b35

Browse files
committed
auto merge of #18406 : thestinger/rust/oom, r=cmr
This makes the low-level allocation API suitable for use cases where out-of-memory conditions need to be handled. Closes #18292 [breaking-change]
2 parents 0c12684 + fea985a commit a294b35

File tree

11 files changed

+49
-63
lines changed

11 files changed

+49
-63
lines changed

src/liballoc/heap.rs

+28-56
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use core::ptr::RawPtr;
12+
1113
// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias`
1214

13-
/// Returns a pointer to `size` bytes of memory.
15+
/// Return a pointer to `size` bytes of memory aligned to `align`.
16+
///
17+
/// On failure, return a null pointer.
1418
///
1519
/// Behavior is undefined if the requested size is 0 or the alignment is not a
1620
/// power of 2. The alignment must be no larger than the largest supported page
@@ -20,8 +24,9 @@ pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
2024
imp::allocate(size, align)
2125
}
2226

23-
/// Extends or shrinks the allocation referenced by `ptr` to `size` bytes of
24-
/// memory.
27+
/// Resize the allocation referenced by `ptr` to `size` bytes.
28+
///
29+
/// On failure, return a null pointer and leave the original allocation intact.
2530
///
2631
/// Behavior is undefined if the requested size is 0 or the alignment is not a
2732
/// power of 2. The alignment must be no larger than the largest supported page
@@ -35,8 +40,7 @@ pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint)
3540
imp::reallocate(ptr, old_size, size, align)
3641
}
3742

38-
/// Extends or shrinks the allocation referenced by `ptr` to `size` bytes of
39-
/// memory in-place.
43+
/// Resize the allocation referenced by `ptr` to `size` bytes.
4044
///
4145
/// If the operation succeeds, it returns `usable_size(size, align)` and if it
4246
/// fails (or is a no-op) it returns `usable_size(old_size, align)`.
@@ -95,7 +99,9 @@ unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
9599
if size == 0 {
96100
EMPTY as *mut u8
97101
} else {
98-
allocate(size, align)
102+
let ptr = allocate(size, align);
103+
if ptr.is_null() { ::oom() }
104+
ptr
99105
}
100106
}
101107

@@ -120,7 +126,7 @@ const MIN_ALIGN: uint = 16;
120126
#[cfg(jemalloc)]
121127
mod imp {
122128
use core::option::{None, Option};
123-
use core::ptr::{RawPtr, null_mut, null};
129+
use core::ptr::{null_mut, null};
124130
use core::num::Int;
125131
use libc::{c_char, c_int, c_void, size_t};
126132
use super::MIN_ALIGN;
@@ -131,10 +137,8 @@ mod imp {
131137

132138
extern {
133139
fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
134-
fn je_rallocx(ptr: *mut c_void, size: size_t,
135-
flags: c_int) -> *mut c_void;
136-
fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t,
137-
flags: c_int) -> size_t;
140+
fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
141+
fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
138142
fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
139143
fn je_nallocx(size: size_t, flags: c_int) -> size_t;
140144
fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void,
@@ -160,21 +164,13 @@ mod imp {
160164
#[inline]
161165
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
162166
let flags = align_to_flags(align);
163-
let ptr = je_mallocx(size as size_t, flags) as *mut u8;
164-
if ptr.is_null() {
165-
::oom()
166-
}
167-
ptr
167+
je_mallocx(size as size_t, flags) as *mut u8
168168
}
169169

170170
#[inline]
171171
pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
172172
let flags = align_to_flags(align);
173-
let ptr = je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8;
174-
if ptr.is_null() {
175-
::oom()
176-
}
177-
ptr
173+
je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8
178174
}
179175

180176
#[inline]
@@ -207,7 +203,6 @@ mod imp {
207203
mod imp {
208204
use core::cmp;
209205
use core::ptr;
210-
use core::ptr::RawPtr;
211206
use libc;
212207
use super::MIN_ALIGN;
213208

@@ -220,31 +215,24 @@ mod imp {
220215
#[inline]
221216
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
222217
if align <= MIN_ALIGN {
223-
let ptr = libc::malloc(size as libc::size_t);
224-
if ptr.is_null() {
225-
::oom();
226-
}
227-
ptr as *mut u8
218+
libc::malloc(size as libc::size_t) as *mut u8
228219
} else {
229220
let mut out = 0 as *mut libc::c_void;
230221
let ret = posix_memalign(&mut out,
231222
align as libc::size_t,
232223
size as libc::size_t);
233224
if ret != 0 {
234-
::oom();
225+
ptr::null_mut()
226+
} else {
227+
out as *mut u8
235228
}
236-
out as *mut u8
237229
}
238230
}
239231

240232
#[inline]
241233
pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
242234
if align <= MIN_ALIGN {
243-
let ptr = libc::realloc(ptr as *mut libc::c_void, size as libc::size_t);
244-
if ptr.is_null() {
245-
::oom();
246-
}
247-
ptr as *mut u8
235+
libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
248236
} else {
249237
let new_ptr = allocate(size, align);
250238
ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size));
@@ -276,7 +264,6 @@ mod imp {
276264
mod imp {
277265
use libc::{c_void, size_t};
278266
use libc;
279-
use core::ptr::RawPtr;
280267
use super::MIN_ALIGN;
281268

282269
extern {
@@ -289,35 +276,18 @@ mod imp {
289276
#[inline]
290277
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
291278
if align <= MIN_ALIGN {
292-
let ptr = libc::malloc(size as size_t);
293-
if ptr.is_null() {
294-
::oom();
295-
}
296-
ptr as *mut u8
279+
libc::malloc(size as size_t) as *mut u8
297280
} else {
298-
let ptr = _aligned_malloc(size as size_t, align as size_t);
299-
if ptr.is_null() {
300-
::oom();
301-
}
302-
ptr as *mut u8
281+
_aligned_malloc(size as size_t, align as size_t) as *mut u8
303282
}
304283
}
305284

306285
#[inline]
307286
pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
308287
if align <= MIN_ALIGN {
309-
let ptr = libc::realloc(ptr as *mut c_void, size as size_t);
310-
if ptr.is_null() {
311-
::oom();
312-
}
313-
ptr as *mut u8
288+
libc::realloc(ptr as *mut c_void, size as size_t) as *mut u8
314289
} else {
315-
let ptr = _aligned_realloc(ptr as *mut c_void, size as size_t,
316-
align as size_t);
317-
if ptr.is_null() {
318-
::oom();
319-
}
320-
ptr as *mut u8
290+
_aligned_realloc(ptr as *mut c_void, size as size_t, align as size_t) as *mut u8
321291
}
322292
}
323293

@@ -348,13 +318,15 @@ mod imp {
348318
mod test {
349319
extern crate test;
350320
use self::test::Bencher;
321+
use core::ptr::RawPtr;
351322
use heap;
352323

353324
#[test]
354325
fn basic_reallocate_inplace_noop() {
355326
unsafe {
356327
let size = 4000;
357328
let ptr = heap::allocate(size, 8);
329+
if ptr.is_null() { ::oom() }
358330
let ret = heap::reallocate_inplace(ptr, size, size, 8);
359331
heap::deallocate(ptr, size, 8);
360332
assert_eq!(ret, heap::usable_size(size, 8));

src/liballoc/lib.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
//! # The Rust core allocation library
1212
//!
1313
//! This is the lowest level library through which allocation in Rust can be
14-
//! performed where the allocation is assumed to succeed. This library will
15-
//! abort the process when allocation fails.
14+
//! performed.
1615
//!
1716
//! This library, like libcore, is not intended for general usage, but rather as
1817
//! a building block of other libraries. The types and interfaces in this
@@ -95,8 +94,10 @@ pub mod boxed;
9594
pub mod arc;
9695
pub mod rc;
9796

98-
/// Common OOM routine used by liballoc
99-
fn oom() -> ! {
97+
/// Common out-of-memory routine
98+
#[cold]
99+
#[inline(never)]
100+
pub fn oom() -> ! {
100101
// FIXME(#14674): This really needs to do something other than just abort
101102
// here, but any printing done must be *guaranteed* to not
102103
// allocate.

src/libarena/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#![feature(unsafe_destructor)]
3232
#![allow(missing_docs)]
3333

34+
extern crate alloc;
35+
3436
use std::cell::{Cell, RefCell};
3537
use std::cmp;
3638
use std::intrinsics::{TyDesc, get_tydesc};
@@ -386,6 +388,7 @@ impl<T> TypedArenaChunk<T> {
386388
let size = calculate_size::<T>(capacity);
387389
let chunk = allocate(size, mem::min_align_of::<TypedArenaChunk<T>>())
388390
as *mut TypedArenaChunk<T>;
391+
if chunk.is_null() { alloc::oom() }
389392
(*chunk).next = next;
390393
(*chunk).capacity = capacity;
391394
chunk

src/libcollections/vec.rs

+3
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ impl<T> Vec<T> {
629629
.expect("capacity overflow");
630630
unsafe {
631631
self.ptr = alloc_or_realloc(self.ptr, self.cap * mem::size_of::<T>(), size);
632+
if self.ptr.is_null() { ::alloc::oom() }
632633
}
633634
self.cap = capacity;
634635
}
@@ -666,6 +667,7 @@ impl<T> Vec<T> {
666667
self.cap * mem::size_of::<T>(),
667668
self.len * mem::size_of::<T>(),
668669
mem::min_align_of::<T>()) as *mut T;
670+
if self.ptr.is_null() { ::alloc::oom() }
669671
}
670672
self.cap = self.len;
671673
}
@@ -988,6 +990,7 @@ impl<T> Vec<T> {
988990
if old_size > size { panic!("capacity overflow") }
989991
unsafe {
990992
self.ptr = alloc_or_realloc(self.ptr, old_size, size);
993+
if self.ptr.is_null() { ::alloc::oom() }
991994
}
992995
self.cap = max(self.cap, 2) * 2;
993996
}

src/librustrt/c_str.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl Clone for CString {
101101
fn clone(&self) -> CString {
102102
let len = self.len() + 1;
103103
let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char;
104-
if buf.is_null() { panic!("out of memory") }
104+
if buf.is_null() { ::alloc::oom() }
105105
unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); }
106106
CString { buf: buf as *const libc::c_char, owns_buffer_: true }
107107
}
@@ -388,7 +388,7 @@ impl ToCStr for [u8] {
388388
unsafe fn to_c_str_unchecked(&self) -> CString {
389389
let self_len = self.len();
390390
let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
391-
if buf.is_null() { panic!("out of memory") }
391+
if buf.is_null() { ::alloc::oom() }
392392

393393
ptr::copy_memory(buf, self.as_ptr(), self_len);
394394
*buf.offset(self_len as int) = 0;

src/librustrt/local_data.rs

+1
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ impl TLDValue {
354354
let box_ptr = unsafe {
355355
let allocation = heap::allocate(mem::size_of::<TLDValueBox<T>>(),
356356
mem::min_align_of::<TLDValueBox<T>>());
357+
if allocation.is_null() { ::alloc::oom() }
357358
let value_box = allocation as *mut TLDValueBox<T>;
358359
ptr::write(value_box, TLDValueBox {
359360
value: value,

src/librustrt/mutex.rs

+2
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ mod imp {
519519
use alloc::heap;
520520
use core::atomic;
521521
use core::ptr;
522+
use core::ptr::RawPtr;
522523
use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
523524
use libc;
524525

@@ -608,6 +609,7 @@ mod imp {
608609

609610
pub unsafe fn init_lock() -> uint {
610611
let block = heap::allocate(CRIT_SECTION_SIZE, 8) as *mut c_void;
612+
if block.is_null() { ::alloc::oom() }
611613
InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT);
612614
return block as uint;
613615
}

src/libstd/c_vec.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ mod tests {
170170
fn malloc(n: uint) -> CVec<u8> {
171171
unsafe {
172172
let mem = libc::malloc(n as libc::size_t);
173-
if mem.is_null() { panic!("out of memory") }
173+
if mem.is_null() { ::alloc::oom() }
174174

175175
CVec::new_with_dtor(mem as *mut u8, n,
176176
proc() { libc::free(mem as *mut libc::c_void); })

src/libstd/collections/hashmap/table.rs

+1
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ impl<K, V> RawTable<K, V> {
607607
"capacity overflow");
608608

609609
let buffer = allocate(size, malloc_alignment);
610+
if buffer.is_null() { ::alloc::oom() }
610611

611612
let hashes = buffer.offset(hash_offset as int) as *mut u64;
612613

src/libsync/deque.rs

+1
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ impl<T: Send> Buffer<T> {
353353
unsafe fn new(log_size: uint) -> Buffer<T> {
354354
let size = buffer_alloc_size::<T>(log_size);
355355
let buffer = allocate(size, min_align_of::<T>());
356+
if buffer.is_null() { ::alloc::oom() }
356357
Buffer {
357358
storage: buffer as *const T,
358359
log_size: log_size,

src/test/run-pass/realloc-16687.rs

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ unsafe fn test_triangle() -> bool {
4949
if PRINT { println!("allocate(size={:u} align={:u})", size, align); }
5050

5151
let ret = heap::allocate(size, align);
52+
if ret.is_null() { alloc::oom() }
5253

5354
if PRINT { println!("allocate(size={:u} align={:u}) ret: 0x{:010x}",
5455
size, align, ret as uint);
@@ -70,6 +71,7 @@ unsafe fn test_triangle() -> bool {
7071
}
7172

7273
let ret = heap::reallocate(ptr, old_size, size, align);
74+
if ret.is_null() { alloc::oom() }
7375

7476
if PRINT {
7577
println!("reallocate(ptr=0x{:010x} old_size={:u} size={:u} align={:u}) \

0 commit comments

Comments
 (0)