Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement graph #16

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
/*
* Copyright FalkorDB Ltd. 2023 - present
* Licensed under the Server Side Public License v1 (SSPLv1).
*/

use std::env;

fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed=BINROOT");
println!("cargo:rerun-if-env-changed=CLANG");

let bin_root = env::var("BINROOT");
if let Ok(bin_root) = bin_root {
Expand Down
44 changes: 44 additions & 0 deletions src/binding/cmutex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright FalkorDB Ltd. 2023 - present
* Licensed under the Server Side Public License v1 (SSPLv1).
*/

use std::{mem::MaybeUninit, ptr::null_mut};

use libc::pthread_mutex_t;

/// Wrap C mutex as we can't use Rust Mutex.
/// Used to lock the matrix only when we apply pending changes.
pub struct CMutex {
mutex: pthread_mutex_t,
}

impl CMutex {
pub fn new() -> Self {
unsafe {
let mut mutex = MaybeUninit::uninit();
libc::pthread_mutex_init(mutex.as_mut_ptr(), null_mut());
Self {
mutex: mutex.assume_init(),
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve safety and error handling in the new method.

The new method correctly initializes the mutex, but there are a few improvements that can be made:

  1. The method should be marked as unsafe due to the use of unsafe operations.
  2. Error handling should be added for the pthread_mutex_init call.
  3. Consider using a const pointer for the mutex attributes instead of null_mut().

Here's a suggested implementation:

pub unsafe fn new() -> Result<Self, std::io::Error> {
    let mut mutex = MaybeUninit::uninit();
    let result = libc::pthread_mutex_init(mutex.as_mut_ptr(), std::ptr::null());
    if result != 0 {
        return Err(std::io::Error::from_raw_os_error(result));
    }
    Ok(Self {
        mutex: mutex.assume_init(),
    })
}

This implementation:

  • Marks the function as unsafe.
  • Returns a Result to handle potential errors.
  • Uses std::ptr::null() instead of null_mut() for the attributes pointer.
  • Checks the return value of pthread_mutex_init and returns an error if initialization fails.


pub fn lock(&mut self) {
unsafe {
libc::pthread_mutex_lock(&mut self.mutex);
}
}

pub fn unlock(&mut self) {
unsafe {
libc::pthread_mutex_unlock(&mut self.mutex);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve safety and error handling in lock and unlock methods.

The lock and unlock methods need several improvements:

  1. Both methods should be marked as unsafe due to the use of unsafe C functions.
  2. Error handling should be added for both pthread_mutex_lock and pthread_mutex_unlock calls.
  3. Consider changing the methods to take &self instead of &mut self to allow multiple locks, which is typical for mutex behavior.

Here's a suggested implementation for both methods:

pub unsafe fn lock(&self) -> Result<(), std::io::Error> {
    let result = libc::pthread_mutex_lock(self.mutex.as_ptr());
    if result != 0 {
        return Err(std::io::Error::from_raw_os_error(result));
    }
    Ok(())
}

pub unsafe fn unlock(&self) -> Result<(), std::io::Error> {
    let result = libc::pthread_mutex_unlock(self.mutex.as_ptr());
    if result != 0 {
        return Err(std::io::Error::from_raw_os_error(result));
    }
    Ok(())
}

These implementations:

  • Mark the functions as unsafe.
  • Return a Result to handle potential errors.
  • Take &self instead of &mut self.
  • Check the return value of the pthread functions and return an error if the operation fails.

}

impl Drop for CMutex {
fn drop(&mut self) {
unsafe { libc::pthread_mutex_destroy(&mut self.mutex) };
}
}
53 changes: 53 additions & 0 deletions src/binding/crwlock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright FalkorDB Ltd. 2023 - present
* Licensed under the Server Side Public License v1 (SSPLv1).
*/

use std::ptr::null_mut;

use libc::{pthread_rwlock_t, PTHREAD_RWLOCK_INITIALIZER};

/// Wrap C rwlock as we can't use Rust RWLock.
/// Used to lock the graph.
pub struct CRWLock {
rwlock: Box<pthread_rwlock_t>,
}

impl CRWLock {
pub fn new() -> Self {
let mut res = CRWLock {
rwlock: Box::new(PTHREAD_RWLOCK_INITIALIZER),
};
unsafe {
libc::pthread_rwlock_init(res.rwlock.as_mut(), null_mut());
}
res
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for pthread_rwlock_init.

The initialization of the rwlock looks correct, but there's no error handling for pthread_rwlock_init. This function can fail in certain conditions (e.g., insufficient memory).

Consider adding error handling:

pub fn new() -> Result<Self, std::io::Error> {
    let mut rwlock = Box::new(PTHREAD_RWLOCK_INITIALIZER);
    let result = unsafe {
        libc::pthread_rwlock_init(rwlock.as_mut(), null_mut())
    };
    if result != 0 {
        Err(std::io::Error::last_os_error())
    } else {
        Ok(CRWLock { rwlock })
    }
}

This change would propagate any initialization errors to the caller.


pub fn acquire_read(&mut self) {
unsafe {
let res = libc::pthread_rwlock_rdlock(self.rwlock.as_mut());
debug_assert!(res == 0, "pthread_rwlock_rdlock failed");
}
}

pub fn acquire_write(&mut self) {
unsafe {
let res = libc::pthread_rwlock_wrlock(self.rwlock.as_mut());
debug_assert!(res == 0, "pthread_rwlock_wrlock failed");
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling for acquire_write.

Similar to acquire_read, this method should handle errors properly and propagate them to the caller. The use of debug_assert! only checks in debug builds.

Suggested improvement:

pub fn acquire_write(&mut self) -> Result<(), std::io::Error> {
    let result = unsafe { libc::pthread_rwlock_wrlock(self.rwlock.as_mut()) };
    if result != 0 {
        Err(std::io::Error::from_raw_os_error(result))
    } else {
        Ok(())
    }
}

This change ensures that any errors during lock acquisition are properly handled and communicated to the caller.


pub fn release(&mut self) {
unsafe {
let res = libc::pthread_rwlock_unlock(self.rwlock.as_mut());
debug_assert!(res == 0, "pthread_rwlock_unlock failed");
}
}
}

impl Drop for CRWLock {
fn drop(&mut self) {
unsafe { libc::pthread_rwlock_destroy(self.rwlock.as_mut()) };
}
}
Loading
Loading