Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS})
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
elseif(component STREQUAL "process")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_text.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_preprocessor.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_process.cmake")
elseif(component STREQUAL "parallel")
find_dependency(Threads)
Expand Down
11 changes: 9 additions & 2 deletions src/lang/process/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME process
PRIVATE_HEADERS error.h
SOURCES spawn.cc command_line.h)
PRIVATE_HEADERS error.h usage.h
SOURCES spawn.cc usage.cc command_line.h)

if(SOURCEMETA_CORE_INSTALL)
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME process)
endif()

target_link_libraries(sourcemeta_core_process PRIVATE sourcemeta::core::text)
target_link_libraries(sourcemeta_core_process PRIVATE sourcemeta::core::numeric)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When SOURCEMETA_CORE_LANG_NUMERIC=OFF, the process target still references the nonexistent sourcemeta::core::numeric target. Make the dependency/platform selection consistent, or require and validate numeric as a process prerequisite before adding this link.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lang/process/CMakeLists.txt, line 10:

<comment>When `SOURCEMETA_CORE_LANG_NUMERIC=OFF`, the process target still references the nonexistent `sourcemeta::core::numeric` target. Make the dependency/platform selection consistent, or require and validate numeric as a process prerequisite before adding this link.</comment>

<file context>
@@ -1,9 +1,14 @@
 endif()
 
 target_link_libraries(sourcemeta_core_process PRIVATE sourcemeta::core::text)
+target_link_libraries(sourcemeta_core_process PRIVATE sourcemeta::core::numeric)
+
+if(WIN32)
</file context>


# Matching the condition the sources compile the Windows implementation under,
# so that a MinGW build does not link what it never calls
if(WIN32 AND NOT MINGW)
target_link_libraries(sourcemeta_core_process PRIVATE psapi)
endif()
1 change: 1 addition & 0 deletions src/lang/process/include/sourcemeta/core/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

// NOLINTBEGIN(misc-include-cleaner)
#include <sourcemeta/core/process_error.h>
#include <sourcemeta/core/process_usage.h>
// NOLINTEND(misc-include-cleaner)

#include <filesystem> // std::filesystem
Expand Down
91 changes: 91 additions & 0 deletions src/lang/process/include/sourcemeta/core/process_usage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#ifndef SOURCEMETA_CORE_PROCESS_USAGE_H_
#define SOURCEMETA_CORE_PROCESS_USAGE_H_

#ifndef SOURCEMETA_CORE_PROCESS_EXPORT
#include <sourcemeta/core/process_export.h>
#endif

#include <chrono> // std::chrono::nanoseconds, std::chrono::system_clock
#include <cstdint> // std::uint64_t
#include <optional> // std::optional

namespace sourcemeta::core {

/// @ingroup process
/// What the running process has consumed so far.
///
/// Every member carries no value where the platform cannot cheaply answer, so
/// that a caller reports nothing rather than a zero it cannot tell apart from
/// a measurement.
struct ProcessUsage {
/// Combined user and system processor time
std::optional<std::chrono::nanoseconds> cpu_time{std::nullopt};
/// Physical memory currently held, in bytes
std::optional<std::uint64_t> resident_bytes{std::nullopt};
/// Address space currently mapped, in bytes
std::optional<std::uint64_t> virtual_bytes{std::nullopt};
};

/// @ingroup process
/// What the running process currently holds open, and how much it may.
///
/// Every member carries no value where the platform cannot cheaply answer, so
/// that a caller reports nothing rather than a zero it cannot tell apart from
/// a measurement.
struct ProcessDescriptors {
/// Open file descriptors, or open handles on platforms that count those
std::optional<std::uint64_t> open{std::nullopt};
/// The ceiling the platform enforces, where there is one and it is finite
std::optional<std::uint64_t> maximum{std::nullopt};
};

/// @ingroup process
///
/// Read what the running process has consumed so far. For example:
///
/// ```cpp
/// #include <sourcemeta/core/process.h>
/// #include <chrono>
/// #include <cassert>
///
/// const auto usage{sourcemeta::core::process_usage()};
/// assert(usage.cpu_time.value() >= std::chrono::nanoseconds::zero());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The example calls .value() on an optional that can be empty. When the platform cannot measure CPU time (as documented at line 17-19), .value() throws std::bad_optional_access. Check has_value() before calling .value(), or use value_or() with a fallback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lang/process/include/sourcemeta/core/process_usage.h, line 52:

<comment>The example calls `.value()` on an optional that can be empty. When the platform cannot measure CPU time (as documented at line 17-19), `.value()` throws `std::bad_optional_access`. Check `has_value()` before calling `.value()`, or use `value_or()` with a fallback.</comment>

<file context>
@@ -45,10 +45,11 @@ struct ProcessDescriptors {
 ///
 /// const auto usage{sourcemeta::core::process_usage()};
-/// assert(usage.cpu_seconds.value() >= 0.0);
+/// assert(usage.cpu_time.value() >= std::chrono::nanoseconds::zero());
 /// ```
 SOURCEMETA_CORE_PROCESS_EXPORT
</file context>
Suggested change
/// assert(usage.cpu_time.value() >= std::chrono::nanoseconds::zero());
/// assert(!usage.cpu_time.has_value() || usage.cpu_time.value() >= std::chrono::nanoseconds::zero());

/// ```
SOURCEMETA_CORE_PROCESS_EXPORT
auto process_usage() noexcept -> ProcessUsage;

/// @ingroup process
///
/// Read what the running process currently holds open. This costs a directory
/// scan on some platforms. For example:
///
/// ```cpp
/// #include <sourcemeta/core/process.h>
/// #include <cassert>
///
/// const auto descriptors{sourcemeta::core::process_descriptors()};
/// assert(descriptors.open.value() > 0);
/// ```
SOURCEMETA_CORE_PROCESS_EXPORT
auto process_descriptors() noexcept -> ProcessDescriptors;

/// @ingroup process
///
/// Read when the running process began. The answer does not change. For
/// example:
///
/// ```cpp
/// #include <sourcemeta/core/process.h>
/// #include <chrono>
/// #include <cassert>
///
/// const auto started{sourcemeta::core::process_start_time()};
/// assert(started.value() <= std::chrono::system_clock::now());
/// ```
SOURCEMETA_CORE_PROCESS_EXPORT
auto process_start_time() noexcept
-> std::optional<std::chrono::system_clock::time_point>;

} // namespace sourcemeta::core

#endif
Loading
Loading