moving stuff and cleaning up
This commit is contained in:
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
println,
|
println,
|
||||||
util::bits::{u32_from_bytes, Be},
|
util::{bits::{u32_from_bytes, Be}, lazy::LazyConst},
|
||||||
};
|
};
|
||||||
use core::{
|
use core::{
|
||||||
fmt::Debug, mem::{size_of, transmute}, ops::Range, slice
|
fmt::Debug, mem::{size_of, transmute}, ops::Range, slice
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAGIC: u32 = 0xd00dfeed;
|
const MAGIC: u32 = 0xd00dfeed;
|
||||||
|
pub static DT: LazyConst<FDT> = LazyConst::new();
|
||||||
|
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
use core::{
|
|
||||||
mem::{size_of, transmute},
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
ptr::null_mut,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FREE_SIZE: usize = size_of::<FreeBlockInfo>() + size_of::<FreePointer>();
|
|
||||||
pub const PTR_SIZE: usize = size_of::<FreePointer>();
|
|
||||||
pub const USED_SIZE: usize = size_of::<BlockInfo>();
|
|
||||||
|
|
||||||
pub struct BlockInfo(usize);
|
|
||||||
|
|
||||||
impl BlockInfo {
|
|
||||||
pub const fn new(prev_used: bool, size: usize) -> Self {
|
|
||||||
Self(prev_used as usize | size)
|
|
||||||
}
|
|
||||||
pub fn prev_used(&self) -> bool {
|
|
||||||
self.0 & 1 == 1
|
|
||||||
}
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
self.0 & !1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UsedPointer(*mut BlockInfo);
|
|
||||||
|
|
||||||
impl Deref for UsedPointer {
|
|
||||||
type Target = BlockInfo;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { transmute(self.0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for UsedPointer {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe { transmute(self.0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct FreeBlockInfo {
|
|
||||||
pub size: usize,
|
|
||||||
pub prev: FreePointer,
|
|
||||||
pub next: FreePointer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FreeBlockInfo {
|
|
||||||
pub fn pointer(&mut self) -> FreePointer {
|
|
||||||
FreePointer(self as *mut FreeBlockInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct FreePointer(*mut FreeBlockInfo);
|
|
||||||
|
|
||||||
impl FreePointer {
|
|
||||||
pub const fn null() -> Self {
|
|
||||||
Self(null_mut())
|
|
||||||
}
|
|
||||||
pub unsafe fn new(addr: *mut u8, info: FreeBlockInfo) -> Self {
|
|
||||||
let ptr = Self(addr as *mut FreeBlockInfo);
|
|
||||||
let len = info.size;
|
|
||||||
*ptr.0 = info;
|
|
||||||
let end = addr.byte_add(len).byte_sub(PTR_SIZE) as *mut FreePointer;
|
|
||||||
*end = ptr;
|
|
||||||
Self(addr as *mut FreeBlockInfo)
|
|
||||||
}
|
|
||||||
pub fn to_used(mut self) -> *mut BlockInfo {
|
|
||||||
self.prev.next = self.next;
|
|
||||||
self.next.prev = self.prev;
|
|
||||||
self.0 as *mut BlockInfo
|
|
||||||
}
|
|
||||||
pub unsafe fn insert_new(&mut self, len: usize) -> *mut BlockInfo {
|
|
||||||
let old = self.0;
|
|
||||||
let new = old.byte_add(len);
|
|
||||||
*new = *old;
|
|
||||||
self.0 = new;
|
|
||||||
self.size = self.size - len;
|
|
||||||
|
|
||||||
self.prev.next = *self;
|
|
||||||
self.next.prev = *self;
|
|
||||||
|
|
||||||
old as *mut BlockInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for FreePointer {
|
|
||||||
type Target = FreeBlockInfo;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { transmute(self.0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for FreePointer {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe { transmute(self.0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FreeBlockIter {
|
|
||||||
end: FreePointer,
|
|
||||||
prev: FreePointer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for FreeBlockIter {
|
|
||||||
// you know, I could've returned &'static mut FreeBlockInfo...
|
|
||||||
// that feels too wrong though
|
|
||||||
type Item = FreePointer;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.prev = unsafe { (*self.prev).next };
|
|
||||||
if self.prev == self.end {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.prev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FreeBlockIter {
|
|
||||||
pub fn from(head: &mut FreeBlockInfo) -> Self {
|
|
||||||
Self {
|
|
||||||
end: FreePointer(head),
|
|
||||||
prev: FreePointer(head),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,67 +7,36 @@
|
|||||||
#![test_runner(crate::test::test_runner)]
|
#![test_runner(crate::test::test_runner)]
|
||||||
#![reexport_test_harness_main = "test_main"]
|
#![reexport_test_harness_main = "test_main"]
|
||||||
|
|
||||||
use crate::allocator::ALLOCATOR;
|
use crate::mem::alloc::ALLOCATOR;
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
use fdt::FDT;
|
use fdt::{DT, FDT};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod allocator;
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
pub mod dev;
|
pub mod dev;
|
||||||
pub mod fdt;
|
pub mod fdt;
|
||||||
pub mod heap;
|
|
||||||
pub mod log;
|
pub mod log;
|
||||||
|
pub mod mem;
|
||||||
pub mod qemu;
|
pub mod qemu;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub fn start(heap_mem: Range<*mut u8>, fdt: FDT) -> ! {
|
pub fn start(heap_mem: Range<*mut u8>, fdt: FDT) -> ! {
|
||||||
#[cfg(test)]
|
DT.init(fdt);
|
||||||
test_main();
|
|
||||||
#[cfg(not(test))]
|
|
||||||
main(heap_mem, fdt);
|
|
||||||
qemu::exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main(heap_mem: Range<*mut u8>, fdt: FDT) {
|
|
||||||
println!("we out here vibin");
|
|
||||||
println!("memory range: {:?}", fdt.mem_range());
|
|
||||||
for node in &fdt {}
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ALLOCATOR.init(heap_mem);
|
ALLOCATOR.init(heap_mem);
|
||||||
}
|
}
|
||||||
ALLOCATOR.print();
|
#[cfg(test)]
|
||||||
println!("----------- vec test:");
|
test_main();
|
||||||
let mut test = alloc::vec![1, 2, 3];
|
#[cfg(not(test))]
|
||||||
test.push(3);
|
main();
|
||||||
let test2 = alloc::vec![-1, -2, -3, -4];
|
qemu::exit(0)
|
||||||
ALLOCATOR.print();
|
}
|
||||||
println!("{:?}", test);
|
|
||||||
println!("{:?}", test2);
|
pub fn main() {
|
||||||
drop(test2);
|
println!("we out here vibin");
|
||||||
drop(test);
|
|
||||||
ALLOCATOR.print();
|
|
||||||
println!("----------- vec vec test:");
|
|
||||||
let mut test = alloc::vec::Vec::new();
|
|
||||||
for i in 0..4 {
|
|
||||||
let n = i * 4;
|
|
||||||
test.push(alloc::vec![n, n + 1, n + 2, n + 3]);
|
|
||||||
}
|
|
||||||
ALLOCATOR.print();
|
|
||||||
println!("{:?}", test);
|
|
||||||
drop(test);
|
|
||||||
ALLOCATOR.print();
|
|
||||||
println!("----------- dealloc test:");
|
|
||||||
for i in 0..1000 {
|
|
||||||
let test2: alloc::vec::Vec<i32> = alloc::vec::Vec::with_capacity(10_000_0000);
|
|
||||||
}
|
|
||||||
ALLOCATOR.print();
|
|
||||||
// for _ in 0..40000000 {}
|
|
||||||
// let x = unsafe { *(0x10000000000 as *mut u8) };
|
|
||||||
// println!("we got {x}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
|
use super::heap::Heap;
|
||||||
|
use crate::util::mutex::{Mutex, MutexGuard};
|
||||||
use core::{alloc::GlobalAlloc, ops::Range};
|
use core::{alloc::GlobalAlloc, ops::Range};
|
||||||
|
|
||||||
use crate::{heap::Heap, util::mutex::Mutex};
|
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
pub static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
pub static ALLOCATOR: Allocator = Allocator::empty();
|
||||||
|
|
||||||
pub struct LockedHeap(Mutex<Heap>);
|
pub struct Allocator(Mutex<Heap>);
|
||||||
|
|
||||||
// should look into why I need this, didn't see it in linked list alloc crate
|
// should look into why I need this, didn't see it in linked list alloc crate
|
||||||
unsafe impl Sync for LockedHeap {}
|
unsafe impl Sync for Allocator {}
|
||||||
|
|
||||||
impl LockedHeap {
|
impl Allocator {
|
||||||
pub const fn empty() -> Self {
|
pub const fn empty() -> Self {
|
||||||
Self(Mutex::new(Heap::empty()))
|
Self(Mutex::new(Heap::empty()))
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,12 @@ impl LockedHeap {
|
|||||||
pub fn print(&self) {
|
pub fn print(&self) {
|
||||||
self.0.lock().print();
|
self.0.lock().print();
|
||||||
}
|
}
|
||||||
|
pub fn heap(&self) -> MutexGuard<Heap> {
|
||||||
|
self.0.lock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for LockedHeap {
|
unsafe impl GlobalAlloc for Allocator {
|
||||||
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
||||||
self.0.lock().alloc(layout)
|
self.0.lock().alloc(layout)
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ use crate::println;
|
|||||||
const ALIGN: usize = 0b1000;
|
const ALIGN: usize = 0b1000;
|
||||||
const ALIGN_MASK: usize = !(ALIGN - 1);
|
const ALIGN_MASK: usize = !(ALIGN - 1);
|
||||||
|
|
||||||
struct BlockInfo(usize);
|
pub struct BlockInfo(usize);
|
||||||
|
|
||||||
impl BlockInfo {
|
impl BlockInfo {
|
||||||
pub const fn new(prev_used: bool, size: usize) -> Self {
|
pub const fn new(prev_used: bool, size: usize) -> Self {
|
||||||
@@ -35,9 +35,9 @@ impl BlockInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlockPointer = *mut BlockInfo;
|
pub type BlockPointer = *mut BlockInfo;
|
||||||
|
|
||||||
struct FreeBlockInfo {
|
pub struct FreeBlockInfo {
|
||||||
info: BlockInfo,
|
info: BlockInfo,
|
||||||
prev: FreePointer,
|
prev: FreePointer,
|
||||||
next: FreePointer,
|
next: FreePointer,
|
||||||
@@ -52,7 +52,7 @@ impl FreeBlockInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FreePointer = *mut FreeBlockInfo;
|
pub type FreePointer = *mut FreeBlockInfo;
|
||||||
|
|
||||||
const FREE_SIZE: usize = size_of::<FreeBlockInfo>() + size_of::<FreePointer>();
|
const FREE_SIZE: usize = size_of::<FreeBlockInfo>() + size_of::<FreePointer>();
|
||||||
const PTR_SIZE: usize = size_of::<FreePointer>();
|
const PTR_SIZE: usize = size_of::<FreePointer>();
|
||||||
@@ -146,7 +146,7 @@ impl Heap {
|
|||||||
return null_mut();
|
return null_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn dealloc(&mut self, ptr: *mut u8, layout: core::alloc::Layout) {
|
pub unsafe fn dealloc(&mut self, ptr: *mut u8, _: core::alloc::Layout) {
|
||||||
let used = ptr.byte_sub(USED_SIZE) as BlockPointer;
|
let used = ptr.byte_sub(USED_SIZE) as BlockPointer;
|
||||||
let mut size = (*used).size();
|
let mut size = (*used).size();
|
||||||
let old_size = size;
|
let old_size = size;
|
||||||
@@ -187,13 +187,13 @@ impl Heap {
|
|||||||
(*n_block).unset_prev_used();
|
(*n_block).unset_prev_used();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_free(&mut self) -> FreeBlockIter {
|
pub fn iter_free(&mut self) -> FreeBlockIter {
|
||||||
FreeBlockIter {
|
FreeBlockIter {
|
||||||
prev: &mut self.head,
|
prev: &mut self.head,
|
||||||
end: &mut self.head,
|
end: &mut self.head,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn iter_block(&mut self) -> BlockIter {
|
pub fn iter_block(&mut self) -> BlockIter {
|
||||||
BlockIter {
|
BlockIter {
|
||||||
cur: self.start as BlockPointer,
|
cur: self.start as BlockPointer,
|
||||||
end: self.end,
|
end: self.end,
|
||||||
@@ -213,7 +213,11 @@ impl Heap {
|
|||||||
if n_block as *mut u8 == self.end {
|
if n_block as *mut u8 == self.end {
|
||||||
n_block = &mut self.head.info;
|
n_block = &mut self.head.info;
|
||||||
}
|
}
|
||||||
let used = if (*n_block).prev_used() {"used"} else {"free"};
|
let used = if (*n_block).prev_used() {
|
||||||
|
"used"
|
||||||
|
} else {
|
||||||
|
"free"
|
||||||
|
};
|
||||||
println!(" - {:?}: {}, size 0x{:x}", block, used, size);
|
println!(" - {:?}: {}, size 0x{:x}", block, used, size);
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
@@ -228,7 +232,7 @@ unsafe fn create_free(addr: FreePointer, info: FreeBlockInfo) {
|
|||||||
*end = addr;
|
*end = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FreeBlockIter {
|
pub struct FreeBlockIter {
|
||||||
end: FreePointer,
|
end: FreePointer,
|
||||||
prev: FreePointer,
|
prev: FreePointer,
|
||||||
}
|
}
|
||||||
@@ -245,7 +249,7 @@ impl Iterator for FreeBlockIter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BlockIter {
|
pub struct BlockIter {
|
||||||
end: *mut u8,
|
end: *mut u8,
|
||||||
cur: BlockPointer,
|
cur: BlockPointer,
|
||||||
}
|
}
|
||||||
2
kernel/src/mem/mod.rs
Normal file
2
kernel/src/mem/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod heap;
|
||||||
|
pub mod alloc;
|
||||||
63
kernel/src/test/mem.rs
Normal file
63
kernel/src/test/mem.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
use crate::ALLOCATOR;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn memory1() {
|
||||||
|
let mut test = alloc::vec![1, 2, 3];
|
||||||
|
assert_eq!(test[0], 1);
|
||||||
|
assert_eq!(test[1], 2);
|
||||||
|
assert_eq!(test[2], 3);
|
||||||
|
test.push(3);
|
||||||
|
assert_eq!(test[0], 1);
|
||||||
|
assert_eq!(test[1], 2);
|
||||||
|
assert_eq!(test[2], 3);
|
||||||
|
assert_eq!(test[3], 3);
|
||||||
|
// check allocator
|
||||||
|
|
||||||
|
let test2 = alloc::vec![-1, -2, -3, -4];
|
||||||
|
assert_eq!(test2[0], -1);
|
||||||
|
assert_eq!(test2[1], -2);
|
||||||
|
assert_eq!(test2[2], -3);
|
||||||
|
assert_eq!(test2[3], -4);
|
||||||
|
|
||||||
|
assert_eq!(test[0], 1);
|
||||||
|
assert_eq!(test[1], 2);
|
||||||
|
assert_eq!(test[2], 3);
|
||||||
|
assert_eq!(test[3], 3);
|
||||||
|
// check allocator
|
||||||
|
|
||||||
|
drop(test2);
|
||||||
|
drop(test);
|
||||||
|
// check allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn memory2() {
|
||||||
|
let mut test = alloc::vec::Vec::new();
|
||||||
|
for i in 0..4 {
|
||||||
|
let n = i * 4;
|
||||||
|
test.push(alloc::vec![n, n + 1, n + 2, n + 3]);
|
||||||
|
}
|
||||||
|
for i in 0..4 {
|
||||||
|
let n = i * 4;
|
||||||
|
assert_eq!(test[i][0], n);
|
||||||
|
assert_eq!(test[i][1], n + 1);
|
||||||
|
assert_eq!(test[i][2], n + 2);
|
||||||
|
assert_eq!(test[i][3], n + 3);
|
||||||
|
}
|
||||||
|
// check allocator
|
||||||
|
|
||||||
|
drop(test);
|
||||||
|
// check allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn memory_reuse() {
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let _: alloc::vec::Vec<i32> = alloc::vec::Vec::with_capacity(10_000_0000);
|
||||||
|
}
|
||||||
|
// check allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_alloc_empty() {
|
||||||
|
ALLOCATOR.heap();
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::{print, println, qemu};
|
use crate::{print, println, qemu};
|
||||||
|
|
||||||
|
mod mem;
|
||||||
|
|
||||||
// SURELY I will not have threading tests that cause problems
|
// SURELY I will not have threading tests that cause problems
|
||||||
// SURELY I don't need to pin the input to test_runner
|
// SURELY I don't need to pin the input to test_runner
|
||||||
static mut TESTS: &[&dyn Testable] = &[];
|
static mut TESTS: &[&dyn Testable] = &[];
|
||||||
@@ -52,18 +54,3 @@ pub fn test_panic(info: &core::panic::PanicInfo) -> ! {
|
|||||||
unsafe { FAILED += 1 };
|
unsafe { FAILED += 1 };
|
||||||
run_tests();
|
run_tests();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test1() {
|
|
||||||
assert_eq!(1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test2() {
|
|
||||||
assert_eq!(1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test3() {
|
|
||||||
assert_eq!(5, 4);
|
|
||||||
}
|
|
||||||
45
kernel/src/util/lazy.rs
Normal file
45
kernel/src/util/lazy.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use core::{mem::zeroed, ops::Deref};
|
||||||
|
|
||||||
|
pub struct LazyConst<T> {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
value: T,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
value: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for LazyConst<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
self.value.as_ref().expect("Value was not assigned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LazyConst<T> {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
unsafe { Self { value: zeroed() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&self, value: T) {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
unsafe {
|
||||||
|
as_mut(self).value = value
|
||||||
|
}
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
unsafe {
|
||||||
|
as_mut(self).value = Some(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_mut<T>(x: &T) -> &mut T {
|
||||||
|
#[allow(invalid_reference_casting)]
|
||||||
|
&mut *((x as *const T) as *mut T)
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod bits;
|
pub mod bits;
|
||||||
|
pub mod lazy;
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
pub mod spin;
|
pub mod spin;
|
||||||
|
|||||||
Reference in New Issue
Block a user