Skip to content

Commit

Permalink
Make management functions thread safe
Browse files Browse the repository at this point in the history
  • Loading branch information
cschwan committed Mar 26, 2024
1 parent 94a81e1 commit e3b1830
Showing 1 changed file with 99 additions and 54 deletions.
153 changes: 99 additions & 54 deletions src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,60 @@ use reqwest::StatusCode;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::Mutex;
use tar::Archive;

fn download_set(name: &str, config: &Config) -> Result<()> {
for url in config.pdfset_urls() {
let response = blocking::get(format!("{url}/{name}.tar.gz"))?;
struct LhapdfData;

if response.status() == StatusCode::NOT_FOUND {
continue;
}
impl LhapdfData {
fn get() -> &'static Mutex<LhapdfData> {
static SINGLETON: Mutex<LhapdfData> = Mutex::new(LhapdfData);
&SINGLETON
}

let content = response.bytes()?;
fn download_set(&self, name: &str, config: &Config) -> Result<()> {
// TODO: this function has a race condition if there multiple processes (not multiple
// threads) that try to create the same file

// TODO: what if multiple threads/processes try to write to the same file?
Archive::new(GzDecoder::new(&content[..])).unpack(config.lhapdf_data_path_write())?;
for url in config.pdfset_urls() {
let response = blocking::get(format!("{url}/{name}.tar.gz"))?;

// we found a PDF set, now it's LHAPDF's turn
break;
}
if response.status() == StatusCode::NOT_FOUND {
continue;
}

Ok(())
}
let content = response.bytes()?;

fn update_pdfsets_index(config: &Config) -> Result<()> {
// empty the `static thread_local` variable sitting in `getPDFIndex` to trigger the
// re-initialization of this variable
ffi::empty_lhaindex();
// TODO: what if multiple threads/processes try to write to the same file?
Archive::new(GzDecoder::new(&content[..])).unpack(config.lhapdf_data_path_write())?;

// download `pdfsets.index`
let content = blocking::get(config.pdfsets_index_url())?.text()?;
// we found a PDF set, now it's LHAPDF's turn
break;
}

let pdfsets_index = PathBuf::from(config.lhapdf_data_path_write()).join("pdfsets.index");
Ok(())
}

// TODO: what if multiple threads/processes try to write to the same file?
File::create(pdfsets_index)?.write_all(content.as_bytes())?;
fn update_pdfsets_index(&self, config: &Config) -> Result<()> {
// TODO: this function has a race condition if there multiple processes (not multiple
// threads) that try to create the same file

Ok(())
}
// empty the `static thread_local` variable sitting in `getPDFIndex` to trigger the
// re-initialization of this variable
ffi::empty_lhaindex();

pub fn pdf_name_and_member_via_lhaid(lhaid: i32) -> Option<(String, i32)> {
// this must be the first call before anything from LHAPDF
let config = Config::get();
// download `pdfsets.index`
let content = blocking::get(config.pdfsets_index_url())?.text()?;

let callable = || {
let pdfsets_index = PathBuf::from(config.lhapdf_data_path_write()).join("pdfsets.index");

// TODO: what if multiple threads/processes try to write to the same file?
File::create(pdfsets_index)?.write_all(content.as_bytes())?;

Ok(())
}

pub fn pdf_name_and_member_via_lhaid(&self, lhaid: i32) -> Option<(String, i32)> {
let_cxx_string!(cxx_setname = "");
ffi::lookup_pdf_setname(lhaid, cxx_setname.as_mut());

Expand All @@ -67,45 +78,75 @@ pub fn pdf_name_and_member_via_lhaid(lhaid: i32) -> Option<(String, i32)> {
} else {
Some((setname.to_owned(), memberid))
}
};
}

fn pdf_with_setname_and_member(&self, setname: &str, member: i32) -> Result<UniquePtr<PDF>> {
let_cxx_string!(cxx_setname = setname.to_string());
Ok(ffi::pdf_with_setname_and_member(&cxx_setname, member)?)
}

fn pdfset_new(&self, setname: &str) -> Result<UniquePtr<PDFSet>> {
let_cxx_string!(cxx_setname = setname);
Ok(ffi::pdfset_new(&cxx_setname)?)
}

callable().or_else(|| {
fn set_verbosity(&self, verbosity: i32) {
// this modifies a `static` variable in C++, beware of threads calling this function at the
// same time
ffi::setVerbosity(verbosity);
}

fn verbosity(&self) -> i32 {
// accesses a `static` variable in C++
ffi::verbosity()
}
}

pub fn pdf_name_and_member_via_lhaid(lhaid: i32) -> Option<(String, i32)> {
// this must be the first call before anything from LHAPDF
let config = Config::get();

// TODO: change return type of this function and handle the error properly
let lock = LhapdfData::get().lock().unwrap();

lock.pdf_name_and_member_via_lhaid(lhaid).or_else(|| {
// TODO: change return type of this function and handle the error properly
update_pdfsets_index(config).unwrap();
callable()
lock.update_pdfsets_index(config).unwrap();
lock.pdf_name_and_member_via_lhaid(lhaid)
})
}

pub fn pdf_with_setname_and_member(setname: &str, member: i32) -> Result<UniquePtr<PDF>> {
// this must be the first call before anything from LHAPDF
let config = Config::get();

let_cxx_string!(cxx_setname = setname.to_string());

let callable = || Ok(ffi::pdf_with_setname_and_member(&cxx_setname, member)?);

callable().or_else(|err: Error| {
// here we rely on exactly matching LHAPDF's exception string
if err.to_string() == format!("Info file not found for PDF set '{setname}'") {
download_set(setname, config).and_then(|()| callable())
} else {
Err(err)
}
})
// TODO: handle error properly
let lock = LhapdfData::get().lock().unwrap();

lock.pdf_with_setname_and_member(setname, member)
.or_else(|err: Error| {
// here we rely on exactly matching LHAPDF's exception string
if err.to_string() == format!("Info file not found for PDF set '{setname}'") {
lock.download_set(setname, config)
.and_then(|()| lock.pdf_with_setname_and_member(setname, member))
} else {
Err(err)
}
})
}

pub fn pdfset_new(setname: &str) -> Result<UniquePtr<PDFSet>> {
// this must be the first call before anything from LHAPDF
let config = Config::get();

let_cxx_string!(cxx_setname = setname);

let callable = || Ok(ffi::pdfset_new(&cxx_setname)?);
// TODO: handle error properly
let lock = LhapdfData::get().lock().unwrap();

callable().or_else(|err: Error| {
lock.pdfset_new(setname).or_else(|err: Error| {
// here we rely on exactly matching LHAPDF's exception string
if err.to_string() == format!("Info file not found for PDF set '{setname}'") {
download_set(setname, config).and_then(|()| callable())
lock.download_set(setname, config)
.and_then(|()| lock.pdfset_new(setname))
} else {
Err(err)
}
Expand All @@ -116,14 +157,18 @@ pub fn set_verbosity(verbosity: i32) {
// this must be the first call before anything from LHAPDF
let _ = Config::get();

// TODO: this modifies a `static` variable in C++, beware of threads calling this function at
// the same time
ffi::setVerbosity(verbosity)
// TODO: handle error properly
let lock = LhapdfData::get().lock().unwrap();

lock.set_verbosity(verbosity);
}

pub fn verbosity() -> i32 {
// this must be the first call before anything from LHAPDF
let _ = Config::get();

ffi::verbosity()
// TODO: handle error properly
let lock = LhapdfData::get().lock().unwrap();

lock.verbosity()
}

0 comments on commit e3b1830

Please sign in to comment.