Skip to content

Commit ef87246

Browse files
authored
Fix memory leak when reading properties and avoid memory allocation (tikv#622)
1 parent 59434f9 commit ef87246

File tree

1 file changed

+63
-72
lines changed

1 file changed

+63
-72
lines changed

src/db.rs

Lines changed: 63 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,40 +1558,54 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
15581558
Ok(())
15591559
}
15601560

1561-
/// Retrieves a RocksDB property by name.
1561+
/// Implementation for property_value et al methods.
15621562
///
1563-
/// Full list of properties could be find
1564-
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
1565-
pub fn property_value(&self, name: &str) -> Result<Option<String>, Error> {
1566-
let prop_name = match CString::new(name) {
1567-
Ok(c) => c,
1563+
/// `name` is the name of the property. It will be converted into a CString
1564+
/// and passed to `get_property` as argument. `get_property` reads the
1565+
/// specified property and either returns NULL or a pointer to a C allocated
1566+
/// string; this method takes ownership of that string and will free it at
1567+
/// the end. That string is parsed using `parse` callback which produces
1568+
/// the returned result.
1569+
fn property_value_impl<R>(
1570+
name: &str,
1571+
get_property: impl FnOnce(*const c_char) -> *mut c_char,
1572+
parse: impl FnOnce(&str) -> Result<R, Error>,
1573+
) -> Result<Option<R>, Error> {
1574+
let value = match CString::new(name) {
1575+
Ok(prop_name) => get_property(prop_name.as_ptr()),
15681576
Err(e) => {
15691577
return Err(Error::new(format!(
15701578
"Failed to convert property name to CString: {}",
15711579
e
15721580
)));
15731581
}
15741582
};
1575-
1583+
if value.is_null() {
1584+
return Ok(None);
1585+
}
1586+
let result = match unsafe { CStr::from_ptr(value) }.to_str() {
1587+
Ok(s) => parse(s).map(|value| Some(value)),
1588+
Err(e) => Err(Error::new(format!(
1589+
"Failed to convert property value to string: {}",
1590+
e
1591+
))),
1592+
};
15761593
unsafe {
1577-
let value = ffi::rocksdb_property_value(self.inner, prop_name.as_ptr());
1578-
if value.is_null() {
1579-
return Ok(None);
1580-
}
1581-
1582-
let str_value = match CStr::from_ptr(value).to_str() {
1583-
Ok(s) => s.to_owned(),
1584-
Err(e) => {
1585-
return Err(Error::new(format!(
1586-
"Failed to convert property value to string: {}",
1587-
e
1588-
)));
1589-
}
1590-
};
1591-
15921594
libc::free(value as *mut c_void);
1593-
Ok(Some(str_value))
15941595
}
1596+
result
1597+
}
1598+
1599+
/// Retrieves a RocksDB property by name.
1600+
///
1601+
/// Full list of properties could be find
1602+
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
1603+
pub fn property_value(&self, name: &str) -> Result<Option<String>, Error> {
1604+
Self::property_value_impl(
1605+
name,
1606+
|prop_name| unsafe { ffi::rocksdb_property_value(self.inner, prop_name) },
1607+
|str_value| Ok(str_value.to_owned()),
1608+
)
15951609
}
15961610

15971611
/// Retrieves a RocksDB property by name, for a specific column family.
@@ -1603,53 +1617,34 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
16031617
cf: &impl AsColumnFamilyRef,
16041618
name: &str,
16051619
) -> Result<Option<String>, Error> {
1606-
let prop_name = match CString::new(name) {
1607-
Ok(c) => c,
1608-
Err(e) => {
1609-
return Err(Error::new(format!(
1610-
"Failed to convert property name to CString: {}",
1611-
e
1612-
)));
1613-
}
1614-
};
1615-
1616-
unsafe {
1617-
let value = ffi::rocksdb_property_value_cf(self.inner, cf.inner(), prop_name.as_ptr());
1618-
if value.is_null() {
1619-
return Ok(None);
1620-
}
1621-
1622-
let str_value = match CStr::from_ptr(value).to_str() {
1623-
Ok(s) => s.to_owned(),
1624-
Err(e) => {
1625-
return Err(Error::new(format!(
1626-
"Failed to convert property value to string: {}",
1627-
e
1628-
)));
1629-
}
1630-
};
1620+
Self::property_value_impl(
1621+
name,
1622+
|prop_name| unsafe {
1623+
ffi::rocksdb_property_value_cf(self.inner, cf.inner(), prop_name)
1624+
},
1625+
|str_value| Ok(str_value.to_owned()),
1626+
)
1627+
}
16311628

1632-
libc::free(value as *mut c_void);
1633-
Ok(Some(str_value))
1634-
}
1629+
fn parse_property_int_value(value: &str) -> Result<u64, Error> {
1630+
value.parse::<u64>().map_err(|err| {
1631+
Error::new(format!(
1632+
"Failed to convert property value {} to int: {}",
1633+
value, err
1634+
))
1635+
})
16351636
}
16361637

16371638
/// Retrieves a RocksDB property and casts it to an integer.
16381639
///
16391640
/// Full list of properties that return int values could be find
16401641
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
16411642
pub fn property_int_value(&self, name: &str) -> Result<Option<u64>, Error> {
1642-
match self.property_value(name) {
1643-
Ok(Some(value)) => match value.parse::<u64>() {
1644-
Ok(int_value) => Ok(Some(int_value)),
1645-
Err(e) => Err(Error::new(format!(
1646-
"Failed to convert property value to int: {}",
1647-
e
1648-
))),
1649-
},
1650-
Ok(None) => Ok(None),
1651-
Err(e) => Err(e),
1652-
}
1643+
Self::property_value_impl(
1644+
name,
1645+
|prop_name| unsafe { ffi::rocksdb_property_value(self.inner, prop_name) },
1646+
Self::parse_property_int_value,
1647+
)
16531648
}
16541649

16551650
/// Retrieves a RocksDB property for a specific column family and casts it to an integer.
@@ -1661,17 +1656,13 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
16611656
cf: &impl AsColumnFamilyRef,
16621657
name: &str,
16631658
) -> Result<Option<u64>, Error> {
1664-
match self.property_value_cf(cf, name) {
1665-
Ok(Some(value)) => match value.parse::<u64>() {
1666-
Ok(int_value) => Ok(Some(int_value)),
1667-
Err(e) => Err(Error::new(format!(
1668-
"Failed to convert property value to int: {}",
1669-
e
1670-
))),
1659+
Self::property_value_impl(
1660+
name,
1661+
|prop_name| unsafe {
1662+
ffi::rocksdb_property_value_cf(self.inner, cf.inner(), prop_name)
16711663
},
1672-
Ok(None) => Ok(None),
1673-
Err(e) => Err(e),
1674-
}
1664+
Self::parse_property_int_value,
1665+
)
16751666
}
16761667

16771668
/// The sequence number of the most recent transaction.

0 commit comments

Comments
 (0)