Skip to content

move OnCal to Fun4Cal#4166

Merged
pinkenburg merged 1 commit intosPHENIX-Collaboration:masterfrom
pinkenburg:fun4cal
Feb 6, 2026
Merged

move OnCal to Fun4Cal#4166
pinkenburg merged 1 commit intosPHENIX-Collaboration:masterfrom
pinkenburg:fun4cal

Conversation

@pinkenburg
Copy link
Copy Markdown
Contributor

@pinkenburg pinkenburg commented Feb 5, 2026

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • [X ] New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work for users)
  • Requiring change in macros repository (Please provide links to the macros pull request in the last section)
  • I am a member of GitHub organization of sPHENIX Collaboration, EIC, or ECCE (contact Chris Pinkenburg to join)

What kind of change does this PR introduce? (Bug fix, feature, ...)

This PR moves OnCal to Fun4Cal, renaming the sources appropriately

TODOs (if applicable)

Links to other PRs in macros and calibration repositories (if applicable)

Summary: Move OnCal to Fun4Cal

Motivation & Context:
This PR refactors the calibration framework by introducing a new Fun4Cal subsystem, which replaces/extends the OnCal framework. This represents a structured reorganization of calibration infrastructure to provide improved modularity and database integration for the sPHENIX experiment.

Key Changes:

  • CalReco base class (CalReco.h/.cc): Introduces an abstract base class derived from SubsysReco that serves as the interface for all calibration reconstruction modules. Provides lifecycle hooks (process_event(), End()), database commit/verification methods (CommitToPdbCal(), VerificationOK()), and state management (comment tracking, calibration table management).

  • Fun4CalServer class (Fun4CalServer.h/.cc): Implements a specialized calibration server derived from Fun4AllServer (~2400 lines) with extensive functionality:

    • Run lifecycle management (BeginRun/End with database synchronization)
    • ODBC database integration for run tracking and calibration metadata
    • Calibration workflow: creation, verification, copying across runs, and status tracking
    • Timestamp management for Begin-Of-Run/End-Of-Run synchronization
    • Histogram registration and tracking for calibration monitoring
    • Support for required calibrations and closest-run finding algorithms
  • Supporting infrastructure:

    • Fun4CalDBCodes.h: Database status codes (INIT, STARTED, FAILED, SUCCESS, COPIED variants, COVERED, SUBSYSTEM)
    • Fun4CalHistoBinDefs.h: Histogram bin definitions for run and time ranges
    • Build configuration: Makefile.am, configure.ac, autogen.sh for Autotools integration

Potential Risk Areas:

  • Database format/IO changes: Fun4CalServer introduces ODBC-based database operations. Any schema changes to the calibration DB require careful coordination with existing systems.
  • Backward compatibility: Migration from OnCal to Fun4Cal may require calibration table conversions or database migration scripts (not shown in this PR).
  • Timestamp synchronization: Complex multi-run timestamp alignment logic between OnCal, CalReco, and RunToTime tables could be error-prone if timestamps are inconsistent.
  • Database locking: ODBC operations under heavy load could create contention; consider concurrency/locking strategies for shared calibration databases.
  • External dependencies: New dependency on ODBC (-lodbc++) and pdbcalBase libraries—verify availability and version compatibility across all deployment targets.

Future Improvements:

  • Add comprehensive error handling documentation and recovery procedures for database failures.
  • Implement unit tests for calibration state transitions and timestamp synchronization logic.
  • Consider async/non-blocking database operations for performance optimization.
  • Add migration tooling and documentation for transitioning from OnCal to Fun4Cal.

Note: AI-generated summaries can contain inaccuracies. Cross-reference database interactions and timestamp logic with actual implementation details before deployment.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive calibration reconstruction framework for fun4cal. It defines a base calibration class interface (CalReco) derived from SubsysReco, a feature-rich calibration server (Fun4CalServer) derived from Fun4AllServer with ODBC database integration, enumeration constants for status codes and histogram binning, and autotools build configuration supporting the new library.

Changes

Cohort / File(s) Summary
Calibration Class Framework
calibrations/framework/fun4cal/CalReco.h, calibrations/framework/fun4cal/CalReco.cc
Introduces CalReco base class with lifecycle methods (process_event, End), pure virtual methods for commit/verification (CommitToPdbCal, VerificationOK, CommitedToPdbCalOK), state management (alldone flag, comment aggregation), and accessors for calibration table metadata (pdbcaltables, pdbcalclasses, bankids, localfilelist).
Database Status and Histogram Definitions
calibrations/framework/fun4cal/Fun4CalDBCodes.h, calibrations/framework/fun4cal/Fun4CalHistoBinDefs.h
Adds enum constants for calibration database status codes (INIT, STARTED, FAILED, SUCCESS, COPIEDPREVIOUS, COPIEDLATER, COVERED, SUBSYSTEM) and histogram binning reference points (FIRSTRUNBIN, LASTRUNBIN, BORTIMEBIN, EORTIMEBIN, LASTBINPLUSONE).
Calibration Server
calibrations/framework/fun4cal/Fun4CalServer.h, calibrations/framework/fun4cal/Fun4CalServer.cc
Implements singleton Fun4CalServer extending Fun4AllServer with comprehensive calibration management: ODBC database operations (connection, table creation/updates), run lifecycle (BeginRun, End with subsystem initialization and calibration commit), calibration creation and verification workflows, timestamp synchronization across OnCal/CalReco/RunDB tables, run-to-run calibration copying, histogram registration/management, and diagnostic utilities. Tracks run metadata, required calibrators per subsystem, and maintains calibration status in database.
Build System Configuration
calibrations/framework/fun4cal/Makefile.am, calibrations/framework/fun4cal/configure.ac, calibrations/framework/fun4cal/autogen.sh
Adds autotools build configuration defining libfun4cal.la library target with dependencies on fun4all, odbc++, pdbcalBase, and phool; declares public headers and source files; includes build externals test; provides autogen script for configure preparation with standard autoconf/automake/libtoolize sequence.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Comment on lines +9 to +27
CalReco::CalReco(const std::string &Name)
: SubsysReco(Name)
{
}

int CalReco::process_event(PHCompositeNode * /*topNode*/)
{
std::cout << "process_event(PHCompositeNode *topNode) not implemented by daughter class: " << Name() << std::endl;
return -1;
}

int CalReco::End(PHCompositeNode * /*topNode*/)
{
std::cout << "EndOfAnalysis not implemented by subsystem!" << std::endl;
std::cout << "Use this signal for computing your calibrations and commit." << std::endl;
std::cout << "Dont do these operations at EndOfRun since subsystems may be feeded events from different runs." << std::endl;
std::cout << "The number of events is the real parameter here, not the runnumber." << std::endl;
return 0;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Document analysis impact / reprocessing expectation.

This introduces a calibration reconstruction framework and DB workflow; please update the PR description to state expected analysis impact and whether reprocessing is required.
Based on learnings: If the PR changes reconstruction outputs, calibration constants, or simulation behavior, ensure the description states expected analysis impact and whether reprocessing is required.

Comment on lines +10 to +23
class CalReco : public SubsysReco
{
public:
~CalReco() override = default;

// These might be overwritten by everyone...
int process_event(PHCompositeNode *topNode) override;
int End(PHCompositeNode *topNode) override = 0; // Here you analyze and commit (if committing flag is set)

// Thsse control committing to the database...
virtual void CommitToPdbCal(const int value) = 0; // Set the flag for whether EndOfAnalysis will commit or not
virtual int VerificationOK() const = 0; // Tell us whether the new calib is close enough to the old one
virtual int CommitedToPdbCalOK() const = 0; // Tell us whether committing was successful by re-reading the data

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clarify analysis impact and downstream updates for the new CalReco interface.

Line 10 onward introduces a new calibration base class and commit/verification contract. Please update the PR description to state whether calibration constants or reconstruction outputs change and whether reprocessing is required, and call out any downstream macros/code that must migrate to Fun4Cal/CalReco.

Based on learnings: If the PR changes reconstruction outputs, calibration constants, or simulation behavior, ensure the description states expected analysis impact and whether reprocessing is required.
As per coding guidelines: If interfaces change, ask for compatibility notes and any needed downstream updates.

Comment on lines +223 to +368
int Fun4CalServer::BeginRun(const int runno)
{
if (runno <= 0)
{
std::cout << PHWHERE << "Invalid Run Number: " << runno << std::endl;
exit(1);
}
FillRunListFromFileList();
recoConsts *rc = recoConsts::instance();
// we stick to the first runnumber, but after inheriting from
// Fun4All we get a EndRun/BeginRun when the run number changes
// so we have to catch this here
if (RunNumber() != 0)
{
rc->set_IntFlag("RUNNUMBER", RunNumber()); // set rc flag back to previous run
analysed_runs.push_back(runno);
return 0;
}
RunNumber(runno);
std::vector<std::pair<SubsysReco *, PHCompositeNode *> >::iterator iter;
// copy the subsys reco pointers to another set for
// easier search (we only need the pointers to find
// the subsystems with special timestamp/runnumber needs
std::set<SubsysReco *> NeedOtherTimeStamp;
std::map<std::string, std::set<SubsysReco *> >::const_iterator miter;
std::set<SubsysReco *>::const_iterator siter;
for (miter = requiredCalibrators.begin();
miter != requiredCalibrators.end(); ++miter)
{
for (siter = miter->second.begin(); siter != miter->second.end(); ++siter)
{
NeedOtherTimeStamp.insert(*siter);
}
}

int iret;
int i = 0;
int oncalrun = runno;
int fun4allrun = runno;

RunToTime *runTime = RunToTime::instance();
PHTimeStamp *ts = runTime->getBeginTime(fun4allrun);
PHTimeStamp OnCalBORTimeStamp = *ts;
PHTimeStamp Fun4AllBORTimeStamp(OnCalBORTimeStamp);
delete ts;
if (!requiredCalibrators.empty())
{
fun4allrun = FindClosestCalibratedRun(runno);
ts = runTime->getBeginTime(fun4allrun);
Fun4AllBORTimeStamp = *ts;
delete ts;
}

// we have to do the same TDirectory games as in the Init methods
// save the current dir, cd to the subsystem name dir (which was
// created in init) call the InitRun of the module and cd back

gROOT->cd(default_Tdirectory.c_str());
std::string currdir = gDirectory->GetPath();
std::set<std::string> droplist;
for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}
CalReco *oncal = dynamic_cast<CalReco *>((*iter).first);
if (oncal)
{
std::string table = "CalReco";
table += (*iter).first->Name();
check_create_subsystable(table);
insertRunNumInDB(table, runNum);
std::string calibname = (*iter).first->Name();
add_calibrator_to_statustable(calibname);
std::set<int>::const_iterator runiter;
int calibstatus = GetCalibStatus(calibname, runNum);
if (calibstatus > 0 && testmode == 0)
{
std::cout << calibname << " already ran for run " << runNum << std::endl;
droplist.insert(calibname);
unregisterSubsystem(oncal);
unregisterHisto(calibname);
}
else
{
std::ostringstream stringarg;
stringarg << Fun4CalDBCodes::STARTED;
for (runiter = runlist.begin(); runiter != runlist.end(); ++runiter)
{
updateDB(successTable, calibname, stringarg.str(), *runiter);
}
}
}
if (NeedOtherTimeStamp.contains((*iter).first))
{
std::cout << "changing timestamp for " << (*iter).first->Name() << std::endl;
rc->set_IntFlag("RUNNUMBER", fun4allrun);
// rc->set_TimeStamp(Fun4AllBORTimeStamp);
}
else
{
rc->set_IntFlag("RUNNUMBER", oncalrun);
// rc->set_TimeStamp(CalRecoBORTimeStamp);
}
if (!droplist.contains((*iter).first->Name()))
{
iret = (*iter).first->InitRun(TopNode);
if (iret == Fun4AllReturnCodes::ABORTRUN)
{
std::cout << PHWHERE << "Module " << (*iter).first->Name() << " issued Abort Run, exiting" << std::endl;
exit(-1);
}
i += iret;
}
}
gROOT->cd(currdir.c_str());

rc->set_IntFlag("RUNNUMBER", oncalrun);
// rc->set_TimeStamp(CalRecoBORTimeStamp);
if (Fun4CalServerVars->GetBinContent(Fun4CalHistoBinDefs::FIRSTRUNBIN) == 0)
{
Fun4CalServerVars->SetBinContent(Fun4CalHistoBinDefs::FIRSTRUNBIN, runno);
Fun4CalServerVars->SetBinContent(Fun4CalHistoBinDefs::BORTIMEBIN, (Stat_t) OnCalBORTimeStamp.getTics());
}
Fun4CalServerVars->SetBinContent(Fun4CalHistoBinDefs::LASTRUNBIN, (Stat_t) runno);
ts = runTime->getEndTime(runno);
if (ts)
{
Fun4CalServerVars->SetBinContent(Fun4CalHistoBinDefs::EORTIMEBIN, (Stat_t) ts->getTics());
delete ts;
}

// disconnect from DB to save resources on DB machine
// PdbCal leaves the DB connection open (PdbCal will reconnect without
// problem if neccessary)
DisconnectDB();
// finally drop calibrators which have run already from module list
unregisterSubsystemsNow();
return i;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check C++ standard flags in build configuration files
find . -type f \( -name 'Makefile*' -o -name 'configure*' -o -name 'CMakeLists.txt' \) | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1079


🏁 Script executed:

# Search for RunToTime class definition
rg -n 'class RunToTime|RunToTime::getBeginTime' --type cpp --type h

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 136


🏁 Script executed:

# Check for any null checks of getBeginTime results in the codebase
rg -n 'getBeginTime' -A 3 --type cpp --type h | head -50

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 3661


🏁 Script executed:

# Search for std::set::contains usage in the codebase to gauge C++ version
rg -n '\.contains\(' --type cpp --type h | head -30

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 3256


🏁 Script executed:

# Check the configure.ac for fun4cal to see C++ standard
cat calibrations/framework/fun4cal/configure.ac

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 423


🏁 Script executed:

# Check Makefile.am for C++ flags
cat calibrations/framework/fun4cal/Makefile.am

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 926


🏁 Script executed:

# Check root configure.ac for C++ standard
cat configure.ac 2>/dev/null || find . -maxdepth 2 -name 'configure.ac' -exec head -30 {} \;

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Search for any C++ standard flags in the entire configure system
rg -i 'cxx_std|--std=c\+\+|AC_PROG_CXX' configure.ac Makefile.am 2>/dev/null | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check if there's a top-level configuration file
ls -la | grep -E 'configure|CMake|setup'

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Look for README or documentation about build requirements
find . -maxdepth 2 -type f \( -name 'README*' -o -name 'INSTALL*' -o -name '.github' \) 2>/dev/null

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 89


🏁 Script executed:

# Check the actual line numbers in Fun4CalServer.cc around the vulnerable code
sed -n '260,275p' calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 520


🏁 Script executed:

# Check the safer pattern used elsewhere in same file
sed -n '1564,1577p' calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 391


🏁 Script executed:

# Check README for build requirements and C++ standard
head -100 README.md

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 3591


🏁 Script executed:

# Check if there are any CI/workflow files that show build flags
find . -path './.github' -o -path './ci' -o -path './.travis.yml' 2>/dev/null | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 87


🏁 Script executed:

# Look for any compiler flag specification at the top-level
cat Makefile.am 2>/dev/null | head -50

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Search for explicit C++ standard requirements in any file
rg -i 'c\+\+17|c\+\+20|c\+\+2a' --type-list cpp 2>/dev/null | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check GitHub workflows for build configuration
find .github -type f -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 137


🏁 Script executed:

# Look for compiler/build info in workflow files
cat .github/workflows/*.yml 2>/dev/null | grep -i 'cxx\|c++\|compiler' | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 162


🏁 Script executed:

# Check what the build environment actually uses
find . -name '.github' -type d -exec ls -la {} \;

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 321


🏁 Script executed:

# Search for any C++ standard setting in all configure files
find . -name 'configure.ac' -o -name 'configure' -o -name 'Makefile' -type f | xargs grep -l 'std=c++' 2>/dev/null | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check if AC_PROG_CXX specifies a version elsewhere
find . -name 'configure.ac' -type f -exec grep -H 'AC_PROG_CXX\|CXXFLAGS' {} \; 2>/dev/null | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1820


🏁 Script executed:

# Look at the actual line 303 and 312 where contains is used in Fun4CalServer.cc
sed -n '300,315p' calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 675


🏁 Script executed:

# Get the full context around the contains usage to see exact line numbers
sed -n '295,320p' calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 987


🏁 Script executed:

# Search for the exact lines with .contains in Fun4CalServer.cc
grep -n "\.contains(" calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 188


🏁 Script executed:

# Get context around those lines
sed -n '300,315p' calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 675


🏁 Script executed:

# Check OnCalServer.cc for the same issues mentioned in review
grep -n "\.contains(" calibrations/framework/oncal/OnCalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 188


🏁 Script executed:

# Get the getBeginTime calls in OnCalServer.cc context
sed -n '260,280p' calibrations/framework/oncal/OnCalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 762


🏁 Script executed:

# Search for any -std flag or C++ version info in environment or setup scripts
find . -name '*.sh' -o -name '*.bash' -o -name 'setup*' | xargs grep -l 'std=' 2>/dev/null | head -5

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check if there's a top-level makefile or build system that might set standards
ls -la | grep -i make

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check if the codebase explicitly requires C++20
grep -r "c++20\|c++2a\|std=2" . --include="*.ac" --include="*.am" --include="CMake*" 2>/dev/null | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check the git log or any documentation about C++ version requirement
git log --all --oneline -- configure.ac | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Look for any mention in wiki or docs about compiler/C++ requirement
find . -name '*.md' -type f | xargs grep -i 'c++17\|c++20\|compiler' 2>/dev/null | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Final verification: check if there are any comments or patterns suggesting C++20 adoption
grep -r "C++20\|C++17\|std::set::contains" . --include="*.h" --include="*.cc" 2>/dev/null | head -15

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check what version of GCC/Clang the project targets
find . -name '.travis.yml' -o -name '*.yml' -o -name '*.yaml' | xargs cat 2>/dev/null | grep -i 'gcc\|clang\|compiler' | head -10

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


Guard RunToTime lookups and avoid C++20-only contains.

RunToTime::getBeginTime can return null; dereferencing it without a guard will crash. The code dereferences ts at lines 264–267 and 271–273 without null checks, while other parts of the same file properly validate the pointer. Additionally, std::set::contains at lines 322 and 333 requires C++20; the build configuration does not explicitly specify a C++ standard, risking compilation failures. Replace contains() with find() and add null checks before pointer dereference.

🔧 Suggested fix
-  PHTimeStamp *ts = runTime->getBeginTime(fun4allrun);
-  PHTimeStamp OnCalBORTimeStamp = *ts;
-  PHTimeStamp Fun4AllBORTimeStamp(OnCalBORTimeStamp);
-  delete ts;
+  PHTimeStamp *ts = runTime->getBeginTime(fun4allrun);
+  if (!ts)
+  {
+    std::cout << PHWHERE << "Unknown Run " << fun4allrun << std::endl;
+    exit(1);
+  }
+  PHTimeStamp OnCalBORTimeStamp = *ts;
+  PHTimeStamp Fun4AllBORTimeStamp(OnCalBORTimeStamp);
+  delete ts;
   if (!requiredCalibrators.empty())
   {
     fun4allrun = FindClosestCalibratedRun(runno);
     ts = runTime->getBeginTime(fun4allrun);
+    if (!ts)
+    {
+      std::cout << PHWHERE << "Unknown Run " << fun4allrun << std::endl;
+      exit(1);
+    }
     Fun4AllBORTimeStamp = *ts;
     delete ts;
   }
@@
-    if (NeedOtherTimeStamp.contains((*iter).first))
+    if (NeedOtherTimeStamp.find((*iter).first) != NeedOtherTimeStamp.end())
     {
@@
-    if (!droplist.contains((*iter).first->Name()))
+    if (droplist.find((*iter).first->Name()) == droplist.end())
     {

Comment on lines +370 to +503
int Fun4CalServer::End()
{
if (nEvents == 0)
{
std::cout << "No Events read, you probably gave me an empty filelist" << std::endl;
return -1;
}
int i = 0;
std::vector<std::pair<SubsysReco *, PHCompositeNode *> >::iterator iter;
gROOT->cd(default_Tdirectory.c_str());
std::string currdir = gDirectory->GetPath();

for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}
else
{
if (Verbosity() > 2)
{
std::cout << "End: cded to " << newdirname.str().c_str() << std::endl;
}
}
i += (*iter).first->End((*iter).second);
}

gROOT->cd(default_Tdirectory.c_str());
currdir = gDirectory->GetPath();
for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
CalReco *oncal = dynamic_cast<CalReco *>((*iter).first);
if (!oncal)
{
continue;
}
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}

std::string CalibratorName = oncal->Name();

int verificationstatus = oncal->VerificationOK();
int databasecommitstatus = oncal->CommitedToPdbCalOK();

// report success database the status of the calibration
if (recordDB)
{
std::string table = "CalReco";
table += CalibratorName;

std::ostringstream stringarg;
if (databasecommitstatus == Fun4CalDBCodes::SUCCESS)
{
stringarg << Fun4CalDBCodes::COVERED;
}
else
{
stringarg << Fun4CalDBCodes::FAILED;
}
std::set<int>::const_iterator runiter;
for (runiter = runlist.begin(); runiter != runlist.end(); ++runiter)
{
updateDB(successTable, CalibratorName, stringarg.str(), *runiter);
}
// update the first run which was used in the calibration
// with the real status
updateDB(successTable, CalibratorName, databasecommitstatus);

stringarg.str("");
stringarg << databasecommitstatus;
updateDB(table, "committed", stringarg.str(), RunNumber());

stringarg.str("");
stringarg << verificationstatus;
updateDB(table, "verified", stringarg.str(), RunNumber());

odbc::Timestamp stp(time(nullptr));
updateDB(table, "date", stp.toString(), RunNumber());
updateDB(table, "comment", oncal->Comment(), RunNumber());
time_t beginticks = beginTimeStamp.getTics();
stringarg.str("");
stringarg << beginticks;
updateDB(table, "startvaltime", stringarg.str(), RunNumber());
stp.setTime(beginticks);
updateDB(table, "begintime", stp.toString(), RunNumber());
time_t endticks = endTimeStamp.getTics();
stringarg.str("");
stringarg << endticks;
updateDB(table, "endvaltime", stringarg.str(), RunNumber());
stp.setTime(endticks);
updateDB(table, "endtime", stp.toString(), RunNumber());

std::string filelist;
for (Fun4AllSyncManager *sync : SyncManagers)
{
for (Fun4AllInputManager *inmgr : sync->GetInputManagers())
{
for (const std::string &infile : inmgr->GetFileOpenedList())
{
filelist += (infile).substr(((infile).find_last_of('/') + 1), (infile).size());
filelist += " "; // this needs to be stripped again for last entry
}
}
}
filelist.pop_back(); // strip empty space at end from loop
std::cout << "FileList: " << filelist << std::endl;
updateDB(table, "files", filelist, RunNumber());
updateDB(table, "cvstag", cvstag, RunNumber());
}

std::cout << "SERVER SUMMARY: " << oncal->Name() << " "
<< (verificationstatus == 1 ? "Verification: SUCCESS " : "Verification: FAILURE ")
<< (databasecommitstatus == 1 ? "DB commit: SUCCESS " : "DB commit: FAILURE ")
<< std::endl;

printStamps();
}
gROOT->cd(currdir.c_str());
dumpHistos(); // save the histograms in files
return i;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid pop_back() on an empty file list.

If no input files were opened, filelist is empty and pop_back() will crash at End().

🔧 Suggested fix
-      filelist.pop_back();  // strip empty space at end from loop
+      if (!filelist.empty())
+      {
+        filelist.pop_back();  // strip empty space at end from loop
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
int Fun4CalServer::End()
{
if (nEvents == 0)
{
std::cout << "No Events read, you probably gave me an empty filelist" << std::endl;
return -1;
}
int i = 0;
std::vector<std::pair<SubsysReco *, PHCompositeNode *> >::iterator iter;
gROOT->cd(default_Tdirectory.c_str());
std::string currdir = gDirectory->GetPath();
for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}
else
{
if (Verbosity() > 2)
{
std::cout << "End: cded to " << newdirname.str().c_str() << std::endl;
}
}
i += (*iter).first->End((*iter).second);
}
gROOT->cd(default_Tdirectory.c_str());
currdir = gDirectory->GetPath();
for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
CalReco *oncal = dynamic_cast<CalReco *>((*iter).first);
if (!oncal)
{
continue;
}
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}
std::string CalibratorName = oncal->Name();
int verificationstatus = oncal->VerificationOK();
int databasecommitstatus = oncal->CommitedToPdbCalOK();
// report success database the status of the calibration
if (recordDB)
{
std::string table = "CalReco";
table += CalibratorName;
std::ostringstream stringarg;
if (databasecommitstatus == Fun4CalDBCodes::SUCCESS)
{
stringarg << Fun4CalDBCodes::COVERED;
}
else
{
stringarg << Fun4CalDBCodes::FAILED;
}
std::set<int>::const_iterator runiter;
for (runiter = runlist.begin(); runiter != runlist.end(); ++runiter)
{
updateDB(successTable, CalibratorName, stringarg.str(), *runiter);
}
// update the first run which was used in the calibration
// with the real status
updateDB(successTable, CalibratorName, databasecommitstatus);
stringarg.str("");
stringarg << databasecommitstatus;
updateDB(table, "committed", stringarg.str(), RunNumber());
stringarg.str("");
stringarg << verificationstatus;
updateDB(table, "verified", stringarg.str(), RunNumber());
odbc::Timestamp stp(time(nullptr));
updateDB(table, "date", stp.toString(), RunNumber());
updateDB(table, "comment", oncal->Comment(), RunNumber());
time_t beginticks = beginTimeStamp.getTics();
stringarg.str("");
stringarg << beginticks;
updateDB(table, "startvaltime", stringarg.str(), RunNumber());
stp.setTime(beginticks);
updateDB(table, "begintime", stp.toString(), RunNumber());
time_t endticks = endTimeStamp.getTics();
stringarg.str("");
stringarg << endticks;
updateDB(table, "endvaltime", stringarg.str(), RunNumber());
stp.setTime(endticks);
updateDB(table, "endtime", stp.toString(), RunNumber());
std::string filelist;
for (Fun4AllSyncManager *sync : SyncManagers)
{
for (Fun4AllInputManager *inmgr : sync->GetInputManagers())
{
for (const std::string &infile : inmgr->GetFileOpenedList())
{
filelist += (infile).substr(((infile).find_last_of('/') + 1), (infile).size());
filelist += " "; // this needs to be stripped again for last entry
}
}
}
filelist.pop_back(); // strip empty space at end from loop
std::cout << "FileList: " << filelist << std::endl;
updateDB(table, "files", filelist, RunNumber());
updateDB(table, "cvstag", cvstag, RunNumber());
}
std::cout << "SERVER SUMMARY: " << oncal->Name() << " "
<< (verificationstatus == 1 ? "Verification: SUCCESS " : "Verification: FAILURE ")
<< (databasecommitstatus == 1 ? "DB commit: SUCCESS " : "DB commit: FAILURE ")
<< std::endl;
printStamps();
}
gROOT->cd(currdir.c_str());
dumpHistos(); // save the histograms in files
return i;
}
int Fun4CalServer::End()
{
if (nEvents == 0)
{
std::cout << "No Events read, you probably gave me an empty filelist" << std::endl;
return -1;
}
int i = 0;
std::vector<std::pair<SubsysReco *, PHCompositeNode *> >::iterator iter;
gROOT->cd(default_Tdirectory.c_str());
std::string currdir = gDirectory->GetPath();
for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}
else
{
if (Verbosity() > 2)
{
std::cout << "End: cded to " << newdirname.str().c_str() << std::endl;
}
}
i += (*iter).first->End((*iter).second);
}
gROOT->cd(default_Tdirectory.c_str());
currdir = gDirectory->GetPath();
for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter)
{
CalReco *oncal = dynamic_cast<CalReco *>((*iter).first);
if (!oncal)
{
continue;
}
std::ostringstream newdirname;
newdirname << (*iter).second->getName() << "/" << (*iter).first->Name();
if (!gROOT->cd(newdirname.str().c_str()))
{
std::cout << PHWHERE << "Unexpected TDirectory Problem cd'ing to "
<< (*iter).second->getName()
<< " - send e-mail to off-l with your macro" << std::endl;
exit(1);
}
std::string CalibratorName = oncal->Name();
int verificationstatus = oncal->VerificationOK();
int databasecommitstatus = oncal->CommitedToPdbCalOK();
// report success database the status of the calibration
if (recordDB)
{
std::string table = "CalReco";
table += CalibratorName;
std::ostringstream stringarg;
if (databasecommitstatus == Fun4CalDBCodes::SUCCESS)
{
stringarg << Fun4CalDBCodes::COVERED;
}
else
{
stringarg << Fun4CalDBCodes::FAILED;
}
std::set<int>::const_iterator runiter;
for (runiter = runlist.begin(); runiter != runlist.end(); ++runiter)
{
updateDB(successTable, CalibratorName, stringarg.str(), *runiter);
}
// update the first run which was used in the calibration
// with the real status
updateDB(successTable, CalibratorName, databasecommitstatus);
stringarg.str("");
stringarg << databasecommitstatus;
updateDB(table, "committed", stringarg.str(), RunNumber());
stringarg.str("");
stringarg << verificationstatus;
updateDB(table, "verified", stringarg.str(), RunNumber());
odbc::Timestamp stp(time(nullptr));
updateDB(table, "date", stp.toString(), RunNumber());
updateDB(table, "comment", oncal->Comment(), RunNumber());
time_t beginticks = beginTimeStamp.getTics();
stringarg.str("");
stringarg << beginticks;
updateDB(table, "startvaltime", stringarg.str(), RunNumber());
stp.setTime(beginticks);
updateDB(table, "begintime", stp.toString(), RunNumber());
time_t endticks = endTimeStamp.getTics();
stringarg.str("");
stringarg << endticks;
updateDB(table, "endvaltime", stringarg.str(), RunNumber());
stp.setTime(endticks);
updateDB(table, "endtime", stp.toString(), RunNumber());
std::string filelist;
for (Fun4AllSyncManager *sync : SyncManagers)
{
for (Fun4AllInputManager *inmgr : sync->GetInputManagers())
{
for (const std::string &infile : inmgr->GetFileOpenedList())
{
filelist += (infile).substr(((infile).find_last_of('/') + 1), (infile).size());
filelist += " "; // this needs to be stripped again for last entry
}
}
}
if (!filelist.empty())
{
filelist.pop_back(); // strip empty space at end from loop
}
std::cout << "FileList: " << filelist << std::endl;
updateDB(table, "files", filelist, RunNumber());
updateDB(table, "cvstag", cvstag, RunNumber());
}
std::cout << "SERVER SUMMARY: " << oncal->Name() << " "
<< (verificationstatus == 1 ? "Verification: SUCCESS " : "Verification: FAILURE ")
<< (databasecommitstatus == 1 ? "DB commit: SUCCESS " : "DB commit: FAILURE ")
<< std::endl;
printStamps();
}
gROOT->cd(currdir.c_str());
dumpHistos(); // save the histograms in files
return i;
}

Comment on lines +945 to +1022
bool Fun4CalServer::updateDB(const std::string &table, const std::string &column,
const std::string &entry, const int runno, const bool append)
{
if (!DBconnection)
{
connectDB();
}

odbc::Statement *statement = nullptr;

statement = DBconnection->createStatement();

std::string comment;
std::ostringstream cmd;
if (append)
{
odbc::ResultSet *rs = nullptr;
std::ostringstream query;
query << "SELECT * FROM "
<< table
<< " WHERE runnumber = "
<< runno;

try
{
rs = statement->executeQuery(query.str());
}
catch (odbc::SQLException &e)
{
std::cout << "in function Fun4CalServer::updateDB() ... ";
std::cout << "run number " << runno << "not found in DB" << std::endl;
std::cout << e.getMessage() << std::endl;
}

rs->next();
try
{
comment = rs->getString(column);
comment += " "; // add empty space between comments
}
catch (odbc::SQLException &e)
{
std::cout << "in function Fun4CalServer::updateDB() ... " << std::endl;
std::cout << "nothing to append." << std::endl;
std::cout << e.getMessage() << std::endl;
}
delete rs;
}

comment += entry;
cmd << "UPDATE "
<< table
<< " set "
<< column
<< " = '"
<< comment
<< "' WHERE runnumber = "
<< runno;

if (Verbosity() == 1)
{
std::cout << "in function Fun4CalServer::updateDB() ... ";
std::cout << "executin SQL statement ... " << std::endl;
std::cout << cmd.str() << std::endl;
}

try
{
statement->executeUpdate(cmd.str());
}
catch (odbc::SQLException &e)
{
std::cout << e.getMessage() << std::endl;
return false;
}
delete statement;
return true;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the file and check if it exists
find . -name "Fun4CalServer.cc" -type f

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 127


🏁 Script executed:

# If file exists, check the relevant lines (945-1022)
if [ -f "calibrations/framework/fun4cal/Fun4CalServer.cc" ]; then
  wc -l calibrations/framework/fun4cal/Fun4CalServer.cc
  sed -n '945,1022p' calibrations/framework/fun4cal/Fun4CalServer.cc
fi

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1925


🏁 Script executed:

# Search for other uses of executeQuery in the codebase to understand error handling patterns
rg "executeQuery" -A 10 -B 2 --type cpp --type cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 104


🏁 Script executed:

# Look for how ResultSet is used elsewhere to understand null/error handling
rg "ResultSet" -B 3 -A 5 --type cpp --type cc | head -100

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 104


🏁 Script executed:

# Search for executeQuery and ResultSet usage patterns without file type restrictions
rg "executeQuery" -A 10 -B 2 | head -150

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 10123


🏁 Script executed:

# Look for how the library is used elsewhere - check imports and includes
rg "odbc::" -B 1 -A 3 | head -100

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 6480


🏁 Script executed:

# Check if there are other methods in this same file that show proper error handling
rg -A 15 "createStatement\(\)" calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 9288


Fix null pointer dereference in updateDB() append path.

If the SELECT query fails, rs remains nullptr and is dereferenced unconditionally at rs->next(). The exception handler must either return early or the row access must be guarded with a null check.

Suggested fix
     try
     {
       rs = statement->executeQuery(query.str());
     }
     catch (odbc::SQLException &e)
     {
       std::cout << "in function Fun4CalServer::updateDB() ... ";
       std::cout << "run number " << runno << "not found in DB" << std::endl;
       std::cout << e.getMessage() << std::endl;
+      delete statement;
+      return false;
     }
-
-    rs->next();
-    try
-    {
-      comment = rs->getString(column);
-      comment += " ";  // add empty space between comments
-    }
-    catch (odbc::SQLException &e)
-    {
-      std::cout << "in function Fun4CalServer::updateDB() ... " << std::endl;
-      std::cout << "nothing to append." << std::endl;
-      std::cout << e.getMessage() << std::endl;
-    }
+    if (rs && rs->next())
+    {
+      try
+      {
+        comment = rs->getString(column);
+        comment += " ";  // add empty space between comments
+      }
+      catch (odbc::SQLException &e)
+      {
+        std::cout << "in function Fun4CalServer::updateDB() ... " << std::endl;
+        std::cout << "nothing to append." << std::endl;
+        std::cout << e.getMessage() << std::endl;
+      }
+    }
     delete rs;

Comment on lines +1639 to +1724
int Fun4CalServer::CopyTables(const CalReco *calibrator, const int FromRun, const int ToRun, const int commit)
{
int iret = calibrator->CopyTables(FromRun, ToRun, commit);
return iret;
}

int Fun4CalServer::CreateCalibration(CalReco *calibrator, const int myrunnumber, const std::string &what, const int commit)
{
int iret = -1;
runNum = myrunnumber;
SetBorTime(myrunnumber);
SetEorTime(myrunnumber);
if (!connectDB())
{
std::cout << "could not connect to " << database << std::endl;
return -1;
}
add_calibrator_to_statustable(calibrator->Name());
std::string table = "OnCal";
table += calibrator->Name();
check_create_subsystable(table);
odbc::Statement *stmt = DBconnection->createStatement();
std::ostringstream cmd;

cmd << "SELECT runnumber FROM "
<< successTable << " where runnumber = "
<< myrunnumber;
odbc::ResultSet *rs = nullptr;
try
{
rs = stmt->executeQuery(cmd.str());
}
catch (odbc::SQLException &e)
{
std::cout << "Table " << successTable << " does not exist" << std::endl;
return -1;
}
if (!rs->next())
{
insertRunNumInDB(successTable, myrunnumber);
}
delete rs;
cmd.str("");
cmd << "SELECT runnumber FROM "
<< successTable << " where runnumber = "
<< myrunnumber << " and "
<< calibrator->Name() << " <= 0";
try
{
rs = stmt->executeQuery(cmd.str());
}
catch (odbc::SQLException &e)
{
std::cout << PHWHERE << " Exception caught, Message: "
<< e.getMessage() << std::endl;
return -1;
}
if (rs->next() || testmode)
{
std::string tablecomment = "Subsytem provided";
iret = calibrator->CreateCalibration(runnumber, what, tablecomment, commit);
if (!iret)
{
std::cout << "Comment: " << tablecomment << std::endl;
std::cout << "updating oncal status tables for " << runnumber << std::endl;
if (commit)
{
CreateCalibrationUpdateStatus(calibrator, table, tablecomment, Fun4CalDBCodes::SUBSYSTEM);
}
}
else
{
std::cout << "Calibratior " << calibrator->Name() << " for run " << runnumber << " failed" << std::endl;
if (commit)
{
CreateCalibrationUpdateStatus(calibrator, table, tablecomment, Fun4CalDBCodes::FAILED);
}
}
}
else
{
std::cout << PHWHERE << " Run " << runnumber << " is already successfully calibrated for "
<< calibrator->Name() << std::endl;
}
return iret;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n '\brunnumber\b' calibrations/framework/fun4cal/Fun4CalServer.h calibrations/framework/fun4cal/Fun4CalServer.cc | head -50

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 5306


🏁 Script executed:

rg -n '\brunNum\b' calibrations/framework/fun4cal/Fun4CalServer.h calibrations/framework/fun4cal/Fun4CalServer.cc | head -50

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1670


🏁 Script executed:

# Check the class definition to see member variables
sed -n '1,200p' calibrations/framework/fun4cal/Fun4CalServer.h

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 6070


Fix undefined runnumber identifier—use runNum instead.

runnumber is not declared in this scope and will cause a compilation error. The member variable runNum (initialized at line 1648) is the correct identifier to use, consistent with how the class tracks the current run throughout all other methods.

Changes needed
-    iret = calibrator->CreateCalibration(runnumber, what, tablecomment, commit);
+    iret = calibrator->CreateCalibration(runNum, what, tablecomment, commit);
@@
-      std::cout << "updating oncal status tables for " << runnumber << std::endl;
+      std::cout << "updating oncal status tables for " << runNum << std::endl;
@@
-      std::cout << "Calibratior " << calibrator->Name() << " for run " << runnumber << " failed" << std::endl;
+      std::cout << "Calibratior " << calibrator->Name() << " for run " << runNum << " failed" << std::endl;
@@
-    std::cout << PHWHERE << " Run " << runnumber << " is already successfully calibrated for "
+    std::cout << PHWHERE << " Run " << runNum << " is already successfully calibrated for "

Comment on lines +31 to +35
using Fun4AllServer::registerHisto;
void registerHisto(TH1 *h1d, CalReco *Calibrator, const int replace = 0);
void unregisterHisto(const std::string &calibratorname);
void Print(const std::string &what = "ALL") const override;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "Fun4CalServer.h" | head -5

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 126


🏁 Script executed:

fd "Fun4CalServer.h" --type f

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 124


🌐 Web query:

ROOT TH1 histogram ownership rules when storing in containers or servers

💡 Result:

Key ROOT ownership rules for TH1 (esp. when putting them in containers / exposing via servers)

  1. Default: histograms are owned by the current directory (gDirectory)
  • When you new TH1..., ROOT automatically adds it to gDirectory (often the last opened TFile), and that directory will delete it when the directory/file is closed/destructed. [1][2]
  • To prevent that for a specific histogram:
    • h->SetDirectory(nullptr); (then you must delete it). [1][2]
  • To prevent it globally for subsequently-created histograms:
    • TH1::AddDirectory(kFALSE); (then you own them all). [1][3]
  1. “Putting a TH1* in a C++ container does not change ownership
  • std::vector<TH1*>, std::map<...>, etc. just store pointers; they don’t delete unless you do.
  • Best practice: store histograms as std::unique_ptr<TH1> and ensure they are not owned by a TDirectory (use TH1::AddDirectory(false) or h->SetDirectory(nullptr)). [1]
  1. ROOT collections (TList, TObjArray, TFolder, …) may delete contents only if configured to
  • Many ROOT collections have an “owner” mode; e.g. TFolder::SetOwner(true) makes the folder delete objects added to it when the folder is deleted. [4][5]
  • If you enable owner mode, make sure the histogram is not also owned by a TDirectory, otherwise you risk double-deletion (directory deletes it, and the collection also tries to). [1]
  1. Copy/clone doesn’t keep directory association
  • TH1::SetDirectory() is not treated as a real persisted property of the histogram for copy/clone; after Clone() you must SetDirectory(...) again if you want the clone attached to a directory. [2]
  1. Servers (e.g. THttpServer) typically browse objects; they don’t “own” them
  • THttpServer/TRootSniffer can scan the “global” directory content (which includes auto-registered histograms), and you can disable that global scan if you only want explicitly registered items. [6]
  • Practical implication: if you don’t want intermediate histograms showing up, avoid auto-registration (use TH1::AddDirectory(false) / SetDirectory(nullptr)), and register only what you want.

Sources: ROOT object ownership manual/user guide and TH1/TFolder reference docs. [1][2][4][5][6]


🏁 Script executed:

cat -n ./calibrations/framework/fun4cal/Fun4CalServer.h | head -60

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 2160


🏁 Script executed:

cat -n ./calibrations/framework/fun4cal/Fun4CalServer.h | sed -n '120,140p'

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 985


🏁 Script executed:

grep -n "~Fun4CalServer\|destructor" ./calibrations/framework/fun4cal/Fun4CalServer.h

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 109


🏁 Script executed:

find . -type f -name "Fun4CalServer.cpp" | head -1

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

fd "Fun4CalServer.cc" --type f

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 125


🏁 Script executed:

cat -n ./calibrations/framework/fun4cal/Fun4CalServer.cc | grep -A 20 "~Fun4CalServer"

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 694


🏁 Script executed:

wc -l ./calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 132


🏁 Script executed:

grep -n "void Fun4CalServer::registerHisto" ./calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 165


🏁 Script executed:

sed -n '1100,1200p' ./calibrations/framework/fun4cal/Fun4CalServer.cc | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 3718


🏁 Script executed:

sed -n '162,220p' ./calibrations/framework/fun4cal/Fun4CalServer.cc | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 2107


🏁 Script executed:

grep -n "void Fun4CalServer::unregisterHisto" ./calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 152


🏁 Script executed:

find . -type f -name "Fun4AllServer.h" -o -name "Fun4AllServer.cc" | grep -E "\.h$|\.cc$"

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 166


🏁 Script executed:

grep -n "void.*registerHisto" calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 240


🏁 Script executed:

grep -n "void.*registerHisto\|class Fun4AllServer" ./offline/framework/fun4all/Fun4AllServer.h | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 121


🏁 Script executed:

grep -n "std::map.*TH1" ./offline/framework/fun4all/Fun4AllServer.h

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

grep -A 30 "Fun4AllServer::registerHisto" ./offline/framework/fun4all/Fun4AllServer.cc | head -40

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1472


🏁 Script executed:

grep -B 5 -A 15 "ServerHistoManager" ./offline/framework/fun4all/Fun4AllServer.cc | head -40

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1501


🏁 Script executed:

grep -n "Histo\[" ./calibrations/framework/fun4cal/Fun4CalServer.cc | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

grep -B 5 -A 10 "Fun4CalServerVars" ./calibrations/framework/fun4cal/Fun4CalServer.cc | head -40

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1115


🏁 Script executed:

grep -n "SetDirectory\|AddDirectory" ./calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

grep -n "class Fun4AllHistoManager\|void dumpHistos" ./offline/framework/fun4all/Fun4AllServer.h | head -5

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

grep -B 5 -A 15 "~Fun4AllServer\|~Fun4AllHistoManager" ./offline/framework/fun4all/Fun4AllServer.cc | head -40

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 500


🏁 Script executed:

sed -n '/^Fun4AllServer::~Fun4AllServer()/,/^}/p' ./offline/framework/fun4all/Fun4AllServer.cc | head -80

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1478


🏁 Script executed:

grep -n "delete.*Histo\|Histo.*delete" ./calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


Document and fix unclear TH1/CalReco/SubsysReco* ownership semantics.**

Fun4CalServer stores raw pointers in Histo map (line 123) and creates Fun4CalServerVars (line 122) via new TH1D(...) but never deletes them in the destructor (lines 82–86). The destructor only cleans up DBconnection, leaving histogram lifetime management unclear:

  • Does Fun4CalServer own registered histograms and must clean them up?
  • Or do callers retain ownership and must keep them alive until unregistering?

This ambiguity creates risk of memory leaks (if the server should own them) or double-deletion (if ROOT's gDirectory also owns them by default). Currently, ownership is neither stated in the header nor enforced via smart pointers.

Recommended actions:

  1. Document the ownership contract in the class header.
  2. If Fun4CalServer owns histograms: delete them in the destructor (or use std::unique_ptr<TH1>).
  3. If callers own them: use SetDirectory(nullptr) to remove ROOT's default ownership, and document that callers must manage lifetime.

Comment on lines +42 to +47
PHTimeStamp *GetEndValidityTS();

PHTimeStamp *GetBeginValidityTS();
void printStamps();
PHTimeStamp *GetLastGoodRunTS(CalReco *calibrator, const int irun);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "Fun4CalServer.h" -type f

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 126


🏁 Script executed:

head -100 calibrations/framework/fun4cal/Fun4CalServer.h | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 4762


🏁 Script executed:

sed -n '35,55p' calibrations/framework/fun4cal/Fun4CalServer.h | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1031


🏁 Script executed:

grep -n "GetEndValidityTS\|GetBeginValidityTS\|GetLastGoodRunTS" calibrations/framework/fun4cal/Fun4CalServer.h

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 228


🏁 Script executed:

grep -r "GetEndValidityTS\|GetBeginValidityTS\|GetLastGoodRunTS" --include="*.cpp" --include="*.h" calibrations/

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 629


🏁 Script executed:

sed -n '1,200p' calibrations/framework/fun4cal/Fun4CalServer.cpp | cat -n | head -80

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 169


🏁 Script executed:

find . -name "*Fun4CalServer*.cpp" -type f

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

find . -name "*.cpp" -path "*/fun4cal/*" -type f

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

ls -la calibrations/framework/fun4cal/

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 771


🏁 Script executed:

grep -n "PHTimeStamp\|GetEndValidityTS\|GetBeginValidityTS\|GetLastGoodRunTS" calibrations/framework/fun4cal/Fun4CalServer.h | head -30

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 515


🏁 Script executed:

sed -n '100,200p' calibrations/framework/fun4cal/Fun4CalServer.h | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 2440


🏁 Script executed:

sed -n '1,100p' calibrations/framework/fun4cal/Fun4CalServer.cc | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 3460


🏁 Script executed:

grep -A 5 "GetEndValidityTS\|GetBeginValidityTS\|GetLastGoodRunTS" calibrations/framework/fun4cal/Fun4CalServer.cc | head -40

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 569


🏁 Script executed:

wc -l calibrations/framework/fun4cal/Fun4CalServer.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 130


🏁 Script executed:

grep -r "GetEndValidityTS\|GetBeginValidityTS\|GetLastGoodRunTS" --include="*.cc" --include="*.C" --include="*.cpp" --include="*.h" | grep -v "^Binary" | head -20

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1201


🏁 Script executed:

sed -n '100,150p' calibrations/framework/fun4cal/Fun4CalServer.cc | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1976


🏁 Script executed:

sed -n '100,140p' calibrations/framework/fun4cal/Fun4CalServer.cc | grep -A 15 "GetBeginValidityTS"

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 478


🏁 Script executed:

grep -r "GetEndValidityTS\(\)\|GetBeginValidityTS\(\)\|GetLastGoodRunTS" calibrations/ --include="*.cc" --include="*.C" --include="*.cpp" -B 2 -A 3 | head -60

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 2500


🏁 Script executed:

grep -n "delete.*GetEndValidityTS\|delete.*GetBeginValidityTS\|delete.*GetLastGoodRunTS" calibrations/ -r --include="*.cc" --include="*.C" --include="*.cpp"

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

grep -r "GetEndValidityTS\|GetBeginValidityTS\|GetLastGoodRunTS" --include="*.cc" --include="*.C" --include="*.cpp" --include="*.h" | grep -v "^calibrations/framework/fun4cal/Fun4CalServer" | grep -v "^calibrations/framework/oncal/OnCalServer"

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

sed -n '89,115p' calibrations/framework/fun4cal/Fun4CalServer.cc | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 929


🏁 Script executed:

sed -n '120,170p' calibrations/framework/fun4cal/Fun4CalServer.cc | cat -n

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1981


Clarify ownership semantics for allocated timestamps.

Lines 42-47 return newly allocated PHTimeStamp pointers (via new in the implementation), but the ownership transfer to the caller is undocumented. This creates a memory management contract violation—callers must delete these pointers, but there is no documentation or API marker (e.g., comment, unique_ptr) to communicate this responsibility. Additionally, the methods should be marked const since they do not modify the server's state.

🔧 Suggested API clarification
-  PHTimeStamp *GetEndValidityTS();
-
-  PHTimeStamp *GetBeginValidityTS();
+  PHTimeStamp *GetEndValidityTS() const;
+
+  PHTimeStamp *GetBeginValidityTS() const;
   void printStamps();
-  PHTimeStamp *GetLastGoodRunTS(CalReco *calibrator, const int irun);
+  PHTimeStamp *GetLastGoodRunTS(CalReco *calibrator, const int irun) const;

Add documentation comment (e.g., in header or implementation) clarifying that the caller owns the returned pointer and must delete it, or consider returning by value instead of new-allocated pointer.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PHTimeStamp *GetEndValidityTS();
PHTimeStamp *GetBeginValidityTS();
void printStamps();
PHTimeStamp *GetLastGoodRunTS(CalReco *calibrator, const int irun);
PHTimeStamp *GetEndValidityTS() const;
PHTimeStamp *GetBeginValidityTS() const;
void printStamps();
PHTimeStamp *GetLastGoodRunTS(CalReco *calibrator, const int irun) const;

@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit 062f669b7a372955653d0c982d4c9e045c72eed7:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@pinkenburg pinkenburg merged commit e2b5c18 into sPHENIX-Collaboration:master Feb 6, 2026
22 checks passed
@pinkenburg pinkenburg deleted the fun4cal branch February 6, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant