Skip to content

Commit 73526d6

Browse files
Drop object contents rather than deallocating whole object (#57)
The object memory is automatically deallocated, however, anything that has been allocated on the heap (strings, vectors etc.) must be deallocated while we are freeing the object.
1 parent faad01b commit 73526d6

File tree

8 files changed

+102
-41
lines changed

8 files changed

+102
-41
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ cc = "1.0.67"
2121

2222
[features]
2323
zts = []
24+
debug = []
25+
alloc = []
2426

2527
[workspace]
2628
members = [

build.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,25 +121,43 @@ fn main() {
121121
.write_to_file(out_path.join("bindings.rs"))
122122
.expect("Unable to write bindings file.");
123123

124-
if has_zts() {
124+
let configure = Configure::get();
125+
126+
if configure.has_zts() {
125127
println!("cargo:rustc-cfg=feature=\"zts\"");
126128
}
129+
130+
if configure.debug() {
131+
println!("cargo:rustc-cfg=feature=\"debug\"");
132+
}
127133
}
128134

129-
/// Checks if ZTS is enabled.
130-
fn has_zts() -> bool {
131-
let cmd = Command::new("php-config")
135+
struct Configure(String);
136+
137+
impl Configure {
138+
pub fn get() -> Self {
139+
let cmd = Command::new("php-config")
132140
.arg("--configure-options")
133141
.output()
134142
.expect("Unable to run `php-config --configure-options`. Please ensure it is visible in your PATH.");
135143

136-
if !cmd.status.success() {
137-
let stderr =
138-
String::from_utf8(cmd.stderr).unwrap_or_else(|_| String::from("Unable to read stderr"));
139-
panic!("Error running `php -i`: {}", stderr);
144+
if !cmd.status.success() {
145+
let stderr = String::from_utf8(cmd.stderr)
146+
.unwrap_or_else(|_| String::from("Unable to read stderr"));
147+
panic!("Error running `php -i`: {}", stderr);
148+
}
149+
150+
// check for the ZTS feature flag in configure
151+
let stdout =
152+
String::from_utf8(cmd.stdout).expect("Unable to read stdout from `php-config`.");
153+
Self(stdout)
140154
}
141155

142-
// check for the ZTS feature flag in configure
143-
let stdout = String::from_utf8(cmd.stdout).expect("Unable to read stdout from `php-config`.");
144-
stdout.contains("--enable-zts")
156+
pub fn has_zts(&self) -> bool {
157+
self.0.contains("--enable-zts")
158+
}
159+
160+
pub fn debug(&self) -> bool {
161+
self.0.contains("--enable-debug")
162+
}
145163
}

example/skel/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2018"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10-
ext-php-rs = { path = "../../" }
10+
ext-php-rs = { path = "../../", features = ["alloc"] }
1111

1212
[lib]
1313
name = "skel"

example/skel/src/allocator.rs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![doc(hidden)]
22

3-
use ext_php_rs::bindings::{_efree, _emalloc};
3+
use ext_php_rs::php::alloc;
44
use std::alloc::GlobalAlloc;
55

66
/// Global allocator which uses the Zend memory management APIs to allocate memory.
@@ -19,28 +19,13 @@ impl PhpAllocator {
1919

2020
unsafe impl GlobalAlloc for PhpAllocator {
2121
unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
22-
let ptr = _emalloc(
23-
layout.size() as _,
24-
std::ptr::null_mut(),
25-
0,
26-
std::ptr::null_mut(),
27-
0,
28-
) as *mut u8;
29-
30-
// eprintln!("allocating {} bytes at {:?}", layout.size(), ptr);
31-
22+
let ptr = alloc::emalloc(layout);
23+
eprintln!("allocating {:?}: {} bytes", ptr, layout.size());
3224
ptr
3325
}
3426

3527
unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
36-
// eprintln!("deallocating {} bytes at {:?}", layout.size(), ptr);
37-
38-
_efree(
39-
ptr as *mut _,
40-
std::ptr::null_mut(),
41-
0,
42-
std::ptr::null_mut(),
43-
0,
44-
)
28+
eprintln!("freeing {:?}: {} bytes", ptr, layout.size());
29+
alloc::efree(ptr)
4530
}
4631
}

example/skel/src/lib.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
mod allocator;
22

33
use allocator::PhpAllocator;
4-
use ext_php_rs::{
5-
php::{class::ClassEntry, exceptions::PhpException},
6-
php_class,
7-
prelude::*,
8-
};
9-
10-
#[global_allocator]
11-
static GLOBAL: PhpAllocator = PhpAllocator::new();
4+
use ext_php_rs::{php_class, prelude::*};
125

136
// #[php_function]
147
// pub fn hello_world() -> String {
@@ -113,6 +106,9 @@ pub fn test_str(input: &str) -> &str {
113106
// ))
114107
// }
115108

109+
#[global_allocator]
110+
static GLOBAL: PhpAllocator = PhpAllocator::new();
111+
116112
#[php_class]
117113
#[property(test = 0)]
118114
#[property(another = "Hello world")]

src/php/alloc.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Functions relating to the Zend Memory Manager, used to allocate request-bound memory.
2+
3+
use crate::bindings::{_efree, _emalloc};
4+
use std::{alloc::Layout, ffi::c_void};
5+
6+
/// Uses the PHP memory allocator to allocate request-bound memory.
7+
///
8+
/// # Parameters
9+
///
10+
/// * `layout` - The layout of the requested memory.
11+
///
12+
/// # Returns
13+
///
14+
/// A pointer to the memory allocated.
15+
pub fn emalloc(layout: Layout) -> *mut u8 {
16+
// TODO account for alignment
17+
let size = layout.size();
18+
19+
(unsafe {
20+
#[cfg(feature = "debug")]
21+
{
22+
_emalloc(size as _, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0)
23+
}
24+
#[cfg(not(feature = "debug"))]
25+
{
26+
_emalloc(size as _)
27+
}
28+
}) as *mut u8
29+
}
30+
31+
/// Frees a given memory pointer which was allocated through the PHP memory manager.
32+
///
33+
/// # Parameters
34+
///
35+
/// * `ptr` - The pointer to the memory to free.
36+
///
37+
/// # Safety
38+
///
39+
/// Caller must guarantee that the given pointer is valid (aligned and non-null) and
40+
/// was originally allocated through the Zend memory manager.
41+
pub unsafe fn efree(ptr: *mut u8) {
42+
#[cfg(feature = "debug")]
43+
{
44+
_efree(
45+
ptr as *mut c_void,
46+
std::ptr::null_mut(),
47+
0,
48+
std::ptr::null_mut(),
49+
0,
50+
)
51+
}
52+
#[cfg(not(feature = "debug"))]
53+
{
54+
_efree(ptr as *mut c_void)
55+
}
56+
}

src/php/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Objects relating to PHP and the Zend engine.
22
3+
#[cfg(feature = "alloc")]
4+
pub mod alloc;
5+
36
pub mod args;
47
pub mod class;
58
pub mod constants;

src/php/types/object.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
marker::PhantomData,
99
mem::MaybeUninit,
1010
ops::{Deref, DerefMut},
11+
ptr,
1112
sync::atomic::{AtomicBool, Ordering},
1213
};
1314

@@ -414,7 +415,7 @@ impl ZendObjectHandlers {
414415

415416
// Cast to *mut u8 to work in byte offsets
416417
let ptr = (object as *mut u8).offset(0 - offset as isize) as *mut T;
417-
let _ = Box::from_raw(ptr);
418+
ptr::drop_in_place(ptr);
418419

419420
match std_object_handlers.free_obj {
420421
Some(free) => free(object),

0 commit comments

Comments
 (0)