commit bc76ac9eb5f4857790fe903308a467459115e066
parent ae9d40be46e3d4d0e06d706261bc2db1da08dfe4
Author: Eduardo Fontana Lazzari <edufonlaz@gmail.com>
Date: Thu, 27 Nov 2025 19:11:24 +0100
Refactor output stream handling
Open the output stream during the sphor_creation step (instead of after
the Monte Carlo algorithm execution as done previously). This ensures
that any error related to opening the stream occurs early in the
runtime, rather than after the algorithm has finished, which may take
considerable time in some situations and would likely result in loss of
work.
Protect existing files from being overwritten by introducing a new
program argument -f, so overwriting only occurs when explicitly
requested by the user.
Diffstat:
5 files changed, 96 insertions(+), 17 deletions(-)
diff --git a/src/sphor.c b/src/sphor.c
@@ -33,6 +33,10 @@
#include <rsys/logger.h>
#include <rsys/mem_allocator.h>
+#include <errno.h> /* errno and EEXIST */
+#include <fcntl.h> /* open */
+#include <unistd.h> /* close */
+#include <sys/stat.h> /* S_IRUSR & S_IWUSR */
/*******************************************************************************
* Helper functions
******************************************************************************/
@@ -63,12 +67,68 @@ release_sphor
if (NULL != sphor->source_distrib_power) {
ssp_ranst_discrete_ref_put(sphor->source_distrib_power);
}
+ if (NULL != sphor->stream) { fclose(sphor->stream); }
darray_interface_release(&sphor->interfaces);
darray_source_view_release(&sphor->source_views);
MEM_RM(sphor->allocator, sphor);
}
/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+FILE*
+open_output_stream
+ (const struct sphor_create_args* args,
+ const struct sphor* sphor)
+{
+ FILE* fp = NULL;
+ int fd = -1;
+
+ if(args->force) {
+ fp = fopen(args->output_filename, "w");
+ if (NULL == fp) {
+ ERROR
+ (sphor, "Could not open output file `%s'\n", args->output_filename);
+ goto error;
+ }
+ } else {
+ fd = open
+ (args->output_filename,
+ O_CREAT | O_EXCL | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR);
+ if (fd >= 0) {
+ fp = fdopen(fd, "w");
+ if (NULL == fp) {
+ ERROR
+ (sphor, "Could not open output file `%s'\n", args->output_filename);
+ goto error;
+ }
+ } else if (errno == EEXIST) {
+ ERROR
+ (sphor, "The output file `%s' already exists'\n",
+ args->output_filename);
+ goto error;
+ } else {
+ ERROR
+ (sphor,
+ "Unexpected error while opening output file `%s'\n",
+ args->output_filename);
+ goto error;
+ }
+ }
+
+exit:
+ return fp;
+error:
+ if (NULL != fp) {
+ fclose(fp);
+ fp = NULL;
+ } else if (fd >= 0) {
+ close(fd);
+ }
+ goto exit;
+}
+/*******************************************************************************
* Exported functions
******************************************************************************/
res_T
@@ -102,7 +162,6 @@ sphor_create
sphor->allocator = allocator;
sphor->nthreads = args->nthreads;
sphor->samples = args->samples;
- sphor->output_filename = args->output_filename;
if (NULL == args->logger) {
sphor->logger = LOGGER_DEFAULT;
} else {
@@ -111,6 +170,11 @@ sphor_create
darray_interface_init(sphor->allocator, &sphor->interfaces);
darray_source_view_init(sphor->allocator, &sphor->source_views);
+ if (NULL != args->output_filename){
+ sphor->stream = open_output_stream(args, sphor);
+ if (NULL == sphor->stream) { goto error; }
+ } else { sphor->stream = stdout; }
+
/* Load the system and build data structure for Monte Carlo */
res = setup_config(sphor, args);
if (RES_OK != res) { goto error; }
@@ -161,3 +225,4 @@ exit:
error:
goto exit;
}
+
diff --git a/src/sphor.h b/src/sphor.h
@@ -57,9 +57,19 @@ struct sphor_create_args {
integrating algorithm */
char* input_filename; /* May be NULL <=> uses stdin */
char* output_filename; /* May be NULL <=> uses stdout */
+ int force; /* Either force overwriting (1) or return an error (0)
+ if the output file already exists */
};
-#define SPHOR_CREATE_ARGS_DEFAULT__ {NULL, NULL, 0, UINT_MAX, 10000, NULL, NULL}
+#define SPHOR_CREATE_ARGS_DEFAULT__ {\
+ NULL,\
+ NULL,\
+ 0,\
+ UINT_MAX,\
+ 10000,\
+ NULL,\
+ NULL,\
+ 0}
static const struct sphor_create_args SPHOR_CREATE_ARGS_DEFAULT =
SPHOR_CREATE_ARGS_DEFAULT__;
diff --git a/src/sphor_c.h b/src/sphor_c.h
@@ -70,7 +70,7 @@ struct sphor {
integrating algorithm */
/* Output stream (can be NULL: stdout) */
- char* output_filename;
+ FILE* stream;
/* Log */
struct logger* logger;
@@ -99,4 +99,9 @@ setup_geometry_accel_struct
const int view_mask, /* Combination of s3d_scene_view flags */
struct s3d_scene_view** out_view);
+extern LOCAL_SYM FILE* /* Returns NULL if an error occurs */
+open_output_stream
+ (const struct sphor_create_args* args,
+ const struct sphor* sphor);
+
#endif /* SPHOR_C_H */
diff --git a/src/sphor_compute_mvrea.c b/src/sphor_compute_mvrea.c
@@ -699,7 +699,6 @@ MVREA_write_outputs
const struct darray_accum* accums,
size_t nfailures)
{
- FILE* stream = NULL;
size_t samples = 0;
size_t i = 0;
struct accum* accum;
@@ -709,23 +708,19 @@ MVREA_write_outputs
ASSERT(NULL != accum2id);
ASSERT(NULL != accums);
- if (NULL != sphor->output_filename){
- stream = fopen(sphor->output_filename, "w");
- } else { stream = stdout; }
-
samples = sphor->samples - nfailures;
- res = MVREA_write_volumes_outputs(sphor, accum2id, accums, samples, stream);
+ res = MVREA_write_volumes_outputs(sphor, accum2id, accums, samples, sphor->stream);
if (RES_OK != res){ goto error; }
- res = MVREA_write_surfaces_outputs(sphor, accum2id, accums, samples, stream);
+ res = MVREA_write_surfaces_outputs(sphor, accum2id, accums, samples, sphor->stream);
if (RES_OK != res){ goto error; }
accum = (struct accum*)darray_accum_cdata_get(accums);
FOR_EACH(i, 0, darray_accum_size_get(accums)){
accum += i;
accum->n_realizations = samples;
- write_accum_estim(accum, stream);
+ write_accum_estim(accum, sphor->stream);
}
exit:
@@ -734,7 +729,6 @@ error:
goto exit;
}
-
static res_T
compute_MVREA_realization
(struct sphor* sphor,
diff --git a/src/sphor_main.c b/src/sphor_main.c
@@ -42,9 +42,11 @@ struct args {
integrating algorithm */
char* input_filename; /* May be NULL <=> uses stdin */
char* output_filename; /* May be NULL <=> uses stdout */
+ int force; /* Either force overwriting (1) or return an error (0)
+ if the output file already exists */
};
-#define ARGS_DEFAULT__ {0, 0, UINT_MAX, 10000, NULL, NULL}
+#define ARGS_DEFAULT__ {0, 0, UINT_MAX, 10000, NULL, NULL, 0}
static const struct args ARGS_DEFAULT =
ARGS_DEFAULT__;
@@ -54,7 +56,7 @@ usage(FILE* stream)
{
ASSERT(NULL != stream);
fprintf(stream,
- "Usage: star-phor [-hv] [-n samples] [-o output] [-t threads] [file]\n");
+ "Usage: star-phor [-hvf] [-n samples] [-o output] [-t threads] [file]\n");
}
static res_T
@@ -66,7 +68,7 @@ parse_args
unsigned long nreal;
unsigned int nthreads;
int opt = 0;
- char* arglist = "hn:o:t:v";
+ char* arglist = "hfn:o:t:v";
res_T res = RES_OK;
ASSERT(NULL != args);
@@ -93,6 +95,10 @@ parse_args
case 'o':
args->output_filename = optarg;
break;
+ /* Force overwrite */
+ case 'f':
+ args->force = 1;
+ break;
/* Number of samples */
case 'n':
res = cstr_to_ulong(optarg, &nreal);
@@ -148,6 +154,7 @@ main (int argc, char** argv)
sphor_create_args.verbose = args.verbose;
sphor_create_args.samples = args.samples;
sphor_create_args.nthreads = args.nthreads;
+ sphor_create_args.force = args.force;
sphor_create_args.input_filename = args.input_filename;
sphor_create_args.output_filename = args.output_filename;
@@ -158,8 +165,6 @@ main (int argc, char** argv)
res = sphor_run(sphor);
if (RES_OK != res) { goto error; }
- /* TODO write results to output */
-
exit:
if (NULL != sphor) { SPHOR(ref_put(sphor)); }
sz = mem_allocated_size();