Skip to content

Commit eaf5da6

Browse files
committed
use threadlocal random file path for jisonl
1 parent 15b38af commit eaf5da6

File tree

6 files changed

+47
-37
lines changed

6 files changed

+47
-37
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ itertools = "0.14.0"
3535
if_rust_version = "1.0"
3636
konst = "0.3.0" # old one for for rust 1.75
3737
toml = "0.9.5"
38+
rand = "0.9.2"
3839

3940
[patch]
4041

prebindgen-proc-macro/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ syn = { workspace = true }
2121
serde = { workspace = true }
2222
serde_json = { workspace = true }
2323
prebindgen = { workspace = true }
24+
rand = { workspace = true }
2425

2526
[features]
2627
debug = ["prebindgen/debug"]

prebindgen-proc-macro/src/lib.rs

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
use prebindgen::{get_prebindgen_out_dir, Record, RecordKind, SourceLocation, DEFAULT_GROUP_NAME};
1313
use proc_macro::TokenStream;
1414
use quote::quote;
15-
use std::fs::{metadata, OpenOptions};
16-
use std::io::Write;
17-
use std::path::Path;
15+
use std::fs::OpenOptions;
1816
use syn::parse::{Parse, ParseStream};
1917
use syn::spanned::Spanned;
2018
use syn::{DeriveInput, ItemConst, ItemFn, ItemType};
@@ -109,18 +107,38 @@ impl Parse for PrebindgenArgs {
109107
}
110108
}
111109

110+
thread_local! {
111+
static PREBINDGEN_JSONL_PATH: std::cell::RefCell<Option<std::path::PathBuf>> = std::cell::RefCell::new(None);
112+
}
113+
112114
/// Get the full path to `{group}_{pid}_{thread_id}.jsonl` generated in OUT_DIR.
115+
/// Use another approach: make unique file name, create it and in case of sucess
116+
/// store the name in Option<PathBuf> threadlocal variable.
117+
/// If variable is already some, use this value.
118+
/// Form file name by calling get_prebindgen_jsonl_path() + random value
113119
fn get_prebindgen_jsonl_path(name: &str) -> std::path::PathBuf {
114-
let thread_id = std::thread::current().id();
120+
if let Some(p) = PREBINDGEN_JSONL_PATH.with(|path| path.borrow().as_ref().map(|p| p.clone())) {
121+
return p;
122+
}
115123
let process_id = std::process::id();
116-
// Extract numeric thread ID from ThreadId debug representation
117-
let thread_id_str = format!("{thread_id:?}");
118-
let thread_id_num = thread_id_str
119-
.strip_prefix("ThreadId(")
120-
.and_then(|s| s.strip_suffix(")"))
121-
.unwrap_or("0");
122-
123-
get_prebindgen_out_dir().join(format!("{name}_{process_id}_{thread_id_num}.jsonl"))
124+
// Try to really create file and return it only if successful
125+
let new_path = loop {
126+
let random_value = rand::random::<u64>();
127+
let path =
128+
get_prebindgen_out_dir().join(format!("{name}_{process_id}_{random_value}.jsonl"));
129+
if OpenOptions::new()
130+
.create_new(true)
131+
.write(true)
132+
.open(&path)
133+
.is_ok()
134+
{
135+
break path;
136+
}
137+
};
138+
PREBINDGEN_JSONL_PATH.with(|path| {
139+
*path.borrow_mut() = Some(new_path.clone());
140+
});
141+
new_path
124142
}
125143

126144
/// Attribute macro that exports FFI definitions for use in language-specific binding crates.
@@ -174,10 +192,6 @@ pub fn prebindgen(args: TokenStream, input: TokenStream) -> TokenStream {
174192

175193
let group = parsed_args.group;
176194

177-
// Get the full path to the JSONL file
178-
let file_path = get_prebindgen_jsonl_path(&group);
179-
let dest_path = Path::new(&file_path);
180-
181195
// Try to parse as different item types
182196
let (kind, name, content, span) = if let Ok(parsed) = syn::parse::<DeriveInput>(input.clone()) {
183197
// Handle struct, enum, union
@@ -241,21 +255,12 @@ pub fn prebindgen(args: TokenStream, input: TokenStream) -> TokenStream {
241255
parsed_args.cfg.clone(),
242256
);
243257

244-
// Convert record to JSON and append to file in JSON-lines format
245-
if let Ok(json_content) = serde_json::to_string(&new_record) {
246-
if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(dest_path) {
247-
// Check if file is empty (just created or was deleted)
248-
let is_empty = metadata(dest_path).map(|m| m.len() == 0).unwrap_or(true);
249-
250-
if is_empty {
251-
#[cfg(feature = "debug")]
252-
println!("Creating jsonl file: {}", dest_path.display());
253-
}
254-
255-
// Write the record as a single line (JSON-lines format)
256-
let _ = writeln!(file, "{json_content}");
257-
let _ = file.flush();
258-
}
258+
// Get the full path to the JSONL file
259+
let file_path = get_prebindgen_jsonl_path(&group);
260+
if let Err(_) = prebindgen::write_to_jsonl_file(&file_path, &[&new_record]) {
261+
return TokenStream::from(quote! {
262+
compile_error!("Failed to write prebindgen record");
263+
});
259264
}
260265

261266
// Apply cfg attribute to the original code if specified

prebindgen/src/api/batching/feature_filter.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use konst;
21
use std::collections::{HashMap, HashSet};
32

43
use roxygen::roxygen;

prebindgen/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ pub mod collect {
173173
pub use crate::api::collect::destination::Destination;
174174
}
175175

176+
#[doc(hidden)]
177+
pub use utils::jsonl::{write_to_jsonl_file, read_jsonl_file};
176178
#[doc(hidden)]
177179
pub use crate::api::record::Record;
178180
#[doc(hidden)]

prebindgen/src/utils/jsonl.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
//! Serialization utilities for reading and writing records.
22
3-
use std::fs;
3+
use std::fs::{self, OpenOptions};
44
use std::io::Write;
55
use std::path::Path;
66

77
use crate::api::record::Record;
88

99
/// Write a collection of records to a file in JSON-lines format
10-
#[allow(dead_code)]
11-
pub fn write_jsonl_file<P: AsRef<Path>>(
10+
pub fn write_to_jsonl_file<P: AsRef<Path>>(
1211
file_path: P,
13-
records: &[Record],
12+
records: &[&Record],
1413
) -> Result<(), Box<dyn std::error::Error>> {
15-
let mut file = fs::File::create(&file_path)?;
14+
let Ok(mut file) = OpenOptions::new().create(true).append(true).open(file_path) else {
15+
return Err("Failed to open file".into());
16+
};
17+
// Check if file is empty (just created or was deleted)
1618
for record in records {
1719
let json_line = record.to_jsonl_string()?;
1820
writeln!(file, "{json_line}")?;

0 commit comments

Comments
 (0)