docs: normalize source contract comments
This commit is contained in:
parent
40d9d27ba6
commit
dae5ab2d08
13 changed files with 107 additions and 6 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
**ACCEPTED** — architecture decision for the post-Gate C documentation freeze.
|
**ACCEPTED** — architecture decision for the post-Gate C documentation freeze.
|
||||||
|
|
||||||
Current Sparse SfM gates A through G are **PASS / FROZEN**.
|
Current Sparse SfM gates A through G are **PASS / FROZEN**.
|
||||||
Project Database: current schema **v17**; historical v16 remains frozen.
|
Project Database: current schema **v20**; historical v16 remains frozen.
|
||||||
|
|
||||||
This record is normative for the current architecture. It does not introduce
|
This record is normative for the current architecture. It does not introduce
|
||||||
an implementation, a public API, a persistence format or a roadmap commitment.
|
an implementation, a public API, a persistence format or a roadmap commitment.
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,14 @@ int main(int argc, char **argv) {
|
||||||
6. **Déterminisme** : les tests ne dépendent pas de l'heure, du filesystem
|
6. **Déterminisme** : les tests ne dépendent pas de l'heure, du filesystem
|
||||||
ou de l'état réseau (sauf test d'import).
|
ou de l'état réseau (sauf test d'import).
|
||||||
|
|
||||||
|
## Commentaires source
|
||||||
|
|
||||||
|
Les commentaires documentent le pourquoi et les contrats non évidents :
|
||||||
|
invariants, propriété et durée de vie, persistance, ainsi que limites et
|
||||||
|
frontières de ressources. Les API publiques documentent leurs contrats non
|
||||||
|
évidents. Ils ne paraphrasent pas le code ligne par ligne et sont mis à jour
|
||||||
|
avec tout changement de comportement.
|
||||||
|
|
||||||
## Tests unitaires vs tests d'intégration
|
## Tests unitaires vs tests d'intégration
|
||||||
|
|
||||||
`test-visual-index` couvre les descriptors synthétiques, le retrieval ORB réel,
|
`test-visual-index` couvre les descriptors synthétiques, le retrieval ORB réel,
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,13 @@ enum {
|
||||||
LARDON3D_ACQUISITION_CAMPAIGN_REQUEST_VERSION = 1,
|
LARDON3D_ACQUISITION_CAMPAIGN_REQUEST_VERSION = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Derived solely from the frozen source, path, confirmation and group bounds.
|
/* Request wire payload is a deterministic codec, not a memcpy of in-memory structs:
|
||||||
*/
|
- fixed magic + version prefix
|
||||||
|
- fixed-width integer fields
|
||||||
|
- explicit little-endian byte order
|
||||||
|
- bounded text fields and counts
|
||||||
|
- strict decode-side validation rejecting malformed inputs.
|
||||||
|
No native struct layout is used for persistence compatibility. */
|
||||||
#define LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES \
|
#define LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES \
|
||||||
((size_t)LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES * \
|
((size_t)LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES * \
|
||||||
(LARDON3D_ACQUISITION_CAMPAIGN_PATH_CAPACITY + 700u) + \
|
(LARDON3D_ACQUISITION_CAMPAIGN_PATH_CAPACITY + 700u) + \
|
||||||
|
|
@ -41,6 +46,9 @@ Lardon3DTask *lardon3d_project_create_acquisition_campaign_task(
|
||||||
bool lardon3d_project_enqueue_acquisition_campaign(
|
bool lardon3d_project_enqueue_acquisition_campaign(
|
||||||
Lardon3DAppState *state, uint64_t scanset_id,
|
Lardon3DAppState *state, uint64_t scanset_id,
|
||||||
const Lardon3DAcquisitionCampaignTaskRequest *request, uint64_t *task_id);
|
const Lardon3DAcquisitionCampaignTaskRequest *request, uint64_t *task_id);
|
||||||
|
/* Request payload is treated as immutable durable input: task execution and
|
||||||
|
* checkpoint/recovery flow from the encoded blob, not from caller memory after
|
||||||
|
* enqueue. */
|
||||||
bool lardon3d_acquisition_campaign_task_reconstruct(
|
bool lardon3d_acquisition_campaign_task_reconstruct(
|
||||||
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||||
Lardon3DTaskKindBinding *binding);
|
Lardon3DTaskKindBinding *binding);
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,9 @@ Lardon3DTask *lardon3d_task_create_typed(
|
||||||
Lardon3DTaskUserdataDestroy userdata_destroy
|
Lardon3DTaskUserdataDestroy userdata_destroy
|
||||||
);
|
);
|
||||||
void lardon3d_task_destroy(Lardon3DTask *task);
|
void lardon3d_task_destroy(Lardon3DTask *task);
|
||||||
/* Exécute le callback dans le thread appelant. */
|
/* Exécute le callback dans le thread appelant. Le callback est invoqué hors
|
||||||
|
* mutex de tâche; le contract d'exécution et l'état appartiennent à la tâche.
|
||||||
|
*/
|
||||||
bool lardon3d_task_start(
|
bool lardon3d_task_start(
|
||||||
Lardon3DTask *task,
|
Lardon3DTask *task,
|
||||||
Lardon3DResourceGovernor *governor,
|
Lardon3DResourceGovernor *governor,
|
||||||
|
|
@ -137,6 +139,9 @@ bool lardon3d_task_resource_estimate(
|
||||||
const Lardon3DTask *task,
|
const Lardon3DTask *task,
|
||||||
Lardon3DResourceEstimate *estimate
|
Lardon3DResourceEstimate *estimate
|
||||||
);
|
);
|
||||||
|
/* L'exécution ne reçoit pas de politique d'admission : c'est au gouverneur de
|
||||||
|
* confirmer la réservation avant l'exécution.
|
||||||
|
*/
|
||||||
bool lardon3d_task_execution_contract(
|
bool lardon3d_task_execution_contract(
|
||||||
const Lardon3DTask *task,
|
const Lardon3DTask *task,
|
||||||
Lardon3DTaskExecutionContract *contract
|
Lardon3DTaskExecutionContract *contract
|
||||||
|
|
@ -145,7 +150,8 @@ bool lardon3d_task_execution_contract(
|
||||||
* et met à jour le contrat. À appeler uniquement depuis le callback en cours
|
* et met à jour le contrat. À appeler uniquement depuis le callback en cours
|
||||||
* d'exécution. Une réponse WAIT du gouverneur est une indisponibilité
|
* d'exécution. Une réponse WAIT du gouverneur est une indisponibilité
|
||||||
* temporaire : la fonction attend un changement de ressources puis retente
|
* temporaire : la fonction attend un changement de ressources puis retente
|
||||||
* l'admission sans échouer la tâche. Retourne false si la tâche est annulée
|
* l'admission sans échouer la tâche. Les bornes de lot se poursuivent après
|
||||||
|
* cette nouvelle admission. Retourne false si la tâche est annulée
|
||||||
* (TASK_CANCELLED), si le gouverneur répond REJECT ou en cas d'erreur interne
|
* (TASK_CANCELLED), si le gouverneur répond REJECT ou en cas d'erreur interne
|
||||||
* (TASK_FAILED). */
|
* (TASK_FAILED). */
|
||||||
bool lardon3d_task_sequence_break(
|
bool lardon3d_task_sequence_break(
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ Lardon3DTaskQueue *lardon3d_task_queue_create(
|
||||||
Lardon3DResourceGovernor *governor,
|
Lardon3DResourceGovernor *governor,
|
||||||
size_t capacity
|
size_t capacity
|
||||||
);
|
);
|
||||||
|
/* File d'exécution bornée, à ordre d'attente FIFO avec sélection adaptative du
|
||||||
|
* premier travail admissible, et un seul worker: ownership d'ordonnancement et
|
||||||
|
* de backpressure seulement. L'admission des demandes reste au Governneur.
|
||||||
|
*/
|
||||||
void lardon3d_task_queue_destroy(Lardon3DTaskQueue *queue);
|
void lardon3d_task_queue_destroy(Lardon3DTaskQueue *queue);
|
||||||
/* La file devient propriétaire de task uniquement en cas de succès.
|
/* La file devient propriétaire de task uniquement en cas de succès.
|
||||||
Bloquante : attend une place libre si la file est pleine. */
|
Bloquante : attend une place libre si la file est pleine. */
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ constexpr unsigned char magic[8] = {'L', '3', 'D', 'A', 'C', 'T', '1', '\0'};
|
||||||
constexpr size_t max_request_size =
|
constexpr size_t max_request_size =
|
||||||
LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES;
|
LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES;
|
||||||
|
|
||||||
|
/* Codec is transport-safe and deterministic: no native struct serialization is
|
||||||
|
* used, integers are fixed-width with explicit endianness, and strings/counts are
|
||||||
|
* explicitly bounded before write/read. */
|
||||||
struct Writer {
|
struct Writer {
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
size_t left;
|
size_t left;
|
||||||
|
|
@ -206,6 +209,8 @@ bool checkpoint(Context *c, Lardon3DTask *t, uint32_t cursor) {
|
||||||
bool run_impl(Lardon3DTask *t, void *p) {
|
bool run_impl(Lardon3DTask *t, void *p) {
|
||||||
auto *c = static_cast<Context *>(p);
|
auto *c = static_cast<Context *>(p);
|
||||||
Lardon3DProjectDbAcquisitionCampaignTask persisted{};
|
Lardon3DProjectDbAcquisitionCampaignTask persisted{};
|
||||||
|
// Request is reconstructed from persisted blob and treated as immutable durable
|
||||||
|
// input during recovery and replay.
|
||||||
std::vector<unsigned char> blob(max_request_size);
|
std::vector<unsigned char> blob(max_request_size);
|
||||||
if (lardon3d_project_db_load_acquisition_campaign_task(
|
if (lardon3d_project_db_load_acquisition_campaign_task(
|
||||||
c->db, lardon3d_task_id(t), blob.data(), blob.size(), &persisted) !=
|
c->db, lardon3d_task_id(t), blob.data(), blob.size(), &persisted) !=
|
||||||
|
|
@ -218,6 +223,10 @@ bool run_impl(Lardon3DTask *t, void *p) {
|
||||||
return false;
|
return false;
|
||||||
Lardon3DProjectDbAcquisitionCampaignCapture retained{};
|
Lardon3DProjectDbAcquisitionCampaignCapture retained{};
|
||||||
uint64_t resume = 0;
|
uint64_t resume = 0;
|
||||||
|
/* Durable replay uses the stable mapping (task_id, group_id) -> capture_id.
|
||||||
|
* The persisted cursor is the zero-based next-work position: after group N
|
||||||
|
* retention, its numeric value is N and replay materializes group N + 1.
|
||||||
|
*/
|
||||||
auto lr = lardon3d_project_db_load_acquisition_campaign_capture(
|
auto lr = lardon3d_project_db_load_acquisition_campaign_capture(
|
||||||
c->db, lardon3d_task_id(t), group_id, &retained);
|
c->db, lardon3d_task_id(t), group_id, &retained);
|
||||||
if (lr == LARDON3D_PROJECT_DB_OK)
|
if (lr == LARDON3D_PROJECT_DB_OK)
|
||||||
|
|
@ -248,6 +257,10 @@ bool run_impl(Lardon3DTask *t, void *p) {
|
||||||
c->db, lardon3d_task_id(t), group_id, out.groups[0].capture_id,
|
c->db, lardon3d_task_id(t), group_id, out.groups[0].capture_id,
|
||||||
group_id) != LARDON3D_PROJECT_DB_OK)
|
group_id) != LARDON3D_PROJECT_DB_OK)
|
||||||
return lardon3d_task_fail(t, "Rétention de Capture impossible.");
|
return lardon3d_task_fail(t, "Rétention de Capture impossible.");
|
||||||
|
/* Limite de reprise acceptée: entre le retour de S3-E et cette rétention
|
||||||
|
* durable, une identité de capture ne peut pas être déduite à posteriori
|
||||||
|
* depuis les chemins/métadonnées/ID d'image.
|
||||||
|
*/
|
||||||
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_TESTING
|
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_TESTING
|
||||||
const char *after_retention =
|
const char *after_retention =
|
||||||
std::getenv("LARDON3D_TEST_CAMPAIGN_FAIL_AFTER_RETENTION");
|
std::getenv("LARDON3D_TEST_CAMPAIGN_FAIL_AFTER_RETENTION");
|
||||||
|
|
@ -261,6 +274,8 @@ bool run_impl(Lardon3DTask *t, void *p) {
|
||||||
!checkpoint(c, t, group_id))
|
!checkpoint(c, t, group_id))
|
||||||
return lardon3d_task_fail(t, "Checkpoint de campagne impossible.");
|
return lardon3d_task_fail(t, "Checkpoint de campagne impossible.");
|
||||||
if (group_id < c->plan.group_count) {
|
if (group_id < c->plan.group_count) {
|
||||||
|
// sequence_break: libère la réservation courante et renégocie l'admission
|
||||||
|
// via le Governor avant le groupe suivant.
|
||||||
Lardon3DTaskExecutionContract contract{};
|
Lardon3DTaskExecutionContract contract{};
|
||||||
Lardon3DResourceReservation *reservation = nullptr;
|
Lardon3DResourceReservation *reservation = nullptr;
|
||||||
if (!lardon3d_task_sequence_break(t, c->governor, &reservation,
|
if (!lardon3d_task_sequence_break(t, c->governor, &reservation,
|
||||||
|
|
@ -352,6 +367,7 @@ bool request_decode_impl(
|
||||||
unsigned char m[8];
|
unsigned char m[8];
|
||||||
uint32_t version, n, c, rep, select;
|
uint32_t version, n, c, rep, select;
|
||||||
uint64_t imported, maxbytes;
|
uint64_t imported, maxbytes;
|
||||||
|
/* Version 1 is the only accepted codec shape; any mismatch rejects replay. */
|
||||||
if (!q.bytes(m, 8) || std::memcmp(m, magic, 8) || !q.u32(version) ||
|
if (!q.bytes(m, 8) || std::memcmp(m, magic, 8) || !q.u32(version) ||
|
||||||
version != 1 || !q.u32(n) || n == 0 || n > sc ||
|
version != 1 || !q.u32(n) || n == 0 || n > sc ||
|
||||||
n > LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES || !q.u32(c) || c > cc ||
|
n > LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES || !q.u32(c) || c > cc ||
|
||||||
|
|
@ -490,6 +506,7 @@ Lardon3DTask *create_task_impl(
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* C ABI boundary: exceptions are trapped so callers never receive C++ throws. */
|
||||||
extern "C" bool lardon3d_acquisition_campaign_request_encode(
|
extern "C" bool lardon3d_acquisition_campaign_request_encode(
|
||||||
const Lardon3DAcquisitionCampaignTaskRequest *r, unsigned char *out,
|
const Lardon3DAcquisitionCampaignTaskRequest *r, unsigned char *out,
|
||||||
size_t cap, size_t *size) {
|
size_t cap, size_t *size) {
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,8 @@ enum class JpegValidationResult {
|
||||||
|
|
||||||
constexpr size_t JPEG_MAX_CONTAINER_IMAGES = 8u;
|
constexpr size_t JPEG_MAX_CONTAINER_IMAGES = 8u;
|
||||||
|
|
||||||
|
// Structural validation deliberately parses JPEG framing without decoding
|
||||||
|
// pixels, so metadata acceptance does not depend on decoder permissiveness.
|
||||||
bool read_byte(FILE *file, unsigned char &value) {
|
bool read_byte(FILE *file, unsigned char &value) {
|
||||||
const int byte = std::fgetc(file);
|
const int byte = std::fgetc(file);
|
||||||
if (byte == EOF) {
|
if (byte == EOF) {
|
||||||
|
|
@ -234,6 +236,8 @@ JpegValidationResult validate_jpeg_image(FILE *file, bool soi_already_consumed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entropy bytes are not marker payload: FF 00 is stuffing and RSTn stays
|
||||||
|
// within the scan. Only a real following marker resumes marker parsing.
|
||||||
bool in_entropy = false;
|
bool in_entropy = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
unsigned char marker = 0u;
|
unsigned char marker = 0u;
|
||||||
|
|
@ -283,6 +287,9 @@ JpegValidationResult validate_jpeg_image(FILE *file, bool soi_already_consumed,
|
||||||
}
|
}
|
||||||
|
|
||||||
JpegValidationResult validate_jpeg_structure(FILE *file) {
|
JpegValidationResult validate_jpeg_structure(FILE *file) {
|
||||||
|
// Secondary JPEGs are allowed only in the bounded private container form:
|
||||||
|
// primary APP2 contains MPF\0, every image reaches EOI structurally, and
|
||||||
|
// inter-image/final padding is zero only. No image pixels are decoded.
|
||||||
bool primary_has_mpf = false;
|
bool primary_has_mpf = false;
|
||||||
for (size_t image_index = 0u; image_index < JPEG_MAX_CONTAINER_IMAGES; ++image_index) {
|
for (size_t image_index = 0u; image_index < JPEG_MAX_CONTAINER_IMAGES; ++image_index) {
|
||||||
bool image_has_mpf = false;
|
bool image_has_mpf = false;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ static const unsigned char feature_magic[8] = {'L', '3', 'D', 'F', 'E', 'A', 'T'
|
||||||
_Static_assert(sizeof(float) == 4 && FLT_RADIX == 2 && FLT_MANT_DIG == 24 && FLT_MAX_EXP == 128,
|
_Static_assert(sizeof(float) == 4 && FLT_RADIX == 2 && FLT_MANT_DIG == 24 && FLT_MAX_EXP == 128,
|
||||||
"Feature File v1/v2 exige IEEE-754 binary32.");
|
"Feature File v1/v2 exige IEEE-754 binary32.");
|
||||||
|
|
||||||
|
/* L3DFEAT v1/v2 uses explicit fixed-width little-endian headers, never native
|
||||||
|
* structs. Counts, offsets, dimensions, source SHA-256, and parameter
|
||||||
|
* fingerprint bind descriptors to their input; malformed/truncated or unknown
|
||||||
|
* versions are rejected before feature data is consumed. */
|
||||||
|
|
||||||
struct Lardon3DFeatureReader {
|
struct Lardon3DFeatureReader {
|
||||||
int descriptor;
|
int descriptor;
|
||||||
uint64_t keypoint_offset;
|
uint64_t keypoint_offset;
|
||||||
|
|
|
||||||
|
|
@ -608,6 +608,8 @@ static const char schema_capture_provenance_v19[] =
|
||||||
"CHECK(parent_asset_id!=child_asset_id));";
|
"CHECK(parent_asset_id!=child_asset_id));";
|
||||||
|
|
||||||
static const char schema_acquisition_campaign_v20[] =
|
static const char schema_acquisition_campaign_v20[] =
|
||||||
|
/* v20 is additive: the generic Task row owns runtime state, while the
|
||||||
|
typed row owns the immutable campaign request and durable cursor. */
|
||||||
"CREATE TABLE IF NOT EXISTS acquisition_campaign_tasks("
|
"CREATE TABLE IF NOT EXISTS acquisition_campaign_tasks("
|
||||||
"task_id INTEGER PRIMARY KEY REFERENCES tasks(task_id) ON DELETE CASCADE,"
|
"task_id INTEGER PRIMARY KEY REFERENCES tasks(task_id) ON DELETE CASCADE,"
|
||||||
"scanset_id INTEGER NOT NULL REFERENCES scansets(scanset_id),"
|
"scanset_id INTEGER NOT NULL REFERENCES scansets(scanset_id),"
|
||||||
|
|
@ -1107,6 +1109,8 @@ static Lardon3DProjectDbResult migrate(Lardon3DProjectDb *database, unsigned int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == LARDON3D_PROJECT_DB_OK && from_version < 20) {
|
if (result == LARDON3D_PROJECT_DB_OK && from_version < 20) {
|
||||||
|
/* The additive v19->v20 DDL remains inside the surrounding migration
|
||||||
|
transaction; schema metadata advances only after this succeeds. */
|
||||||
result = execute(database, schema_acquisition_campaign_v20,
|
result = execute(database, schema_acquisition_campaign_v20,
|
||||||
"migrate acquisition campaign task v19 to v20");
|
"migrate acquisition campaign task v19 to v20");
|
||||||
#ifdef LARDON3D_PROJECT_DB_TESTING
|
#ifdef LARDON3D_PROJECT_DB_TESTING
|
||||||
|
|
@ -2247,6 +2251,8 @@ Lardon3DProjectDbResult lardon3d_project_db_record_acquisition_campaign_task(
|
||||||
!parameters->request || parameters->request_size == 0 || parameters->request_size > INT_MAX) {
|
!parameters->request || parameters->request_size == 0 || parameters->request_size > INT_MAX) {
|
||||||
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
|
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
/* The generic Task/checkpoint and typed request persist atomically: recovery
|
||||||
|
never observes a valid campaign Task without its immutable request blob. */
|
||||||
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint,
|
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint,
|
||||||
NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, parameters,
|
NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, parameters,
|
||||||
updated_at);
|
updated_at);
|
||||||
|
|
@ -2292,6 +2298,11 @@ Lardon3DProjectDbResult lardon3d_project_db_load_acquisition_campaign_task(
|
||||||
Lardon3DProjectDbResult lardon3d_project_db_retain_acquisition_campaign_capture(
|
Lardon3DProjectDbResult lardon3d_project_db_retain_acquisition_campaign_capture(
|
||||||
Lardon3DProjectDb *database, uint64_t task_id, uint32_t group_id,
|
Lardon3DProjectDb *database, uint64_t task_id, uint32_t group_id,
|
||||||
uint64_t capture_id, uint32_t next_group_id) {
|
uint64_t capture_id, uint32_t next_group_id) {
|
||||||
|
/* S3-E already returned capture_id. This transaction retains the group
|
||||||
|
mapping and advances the one-based cursor together; generic progress and
|
||||||
|
checkpoint advance afterwards. An interruption after S3-E returns and
|
||||||
|
before this retention transaction cannot safely be repaired by guessing
|
||||||
|
identity from paths, hashes, metadata, or image IDs. */
|
||||||
if (!database || !valid_task_id(task_id) || !valid_task_id(capture_id) ||
|
if (!database || !valid_task_id(task_id) || !valid_task_id(capture_id) ||
|
||||||
group_id == 0 || next_group_id != group_id) return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
|
group_id == 0 || next_group_id != group_id) return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
|
||||||
(void)pthread_mutex_lock(&database->mutex);
|
(void)pthread_mutex_lock(&database->mutex);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ constexpr uint32_t kBackendKindLibRaw = 1;
|
||||||
constexpr uint32_t kEncoderKindOpenCvPng = 1;
|
constexpr uint32_t kEncoderKindOpenCvPng = 1;
|
||||||
constexpr size_t kRawFingerprintPayloadSize = 200;
|
constexpr size_t kRawFingerprintPayloadSize = 200;
|
||||||
|
|
||||||
|
// RAW Policy v1 is part of derived scientific identity. Its fixed
|
||||||
|
// little-endian L3DRAWD1 payload is not a pathname-derived source identity.
|
||||||
bool valid_wb(const float wb[4]) {
|
bool valid_wb(const float wb[4]) {
|
||||||
for (size_t i = 0; i < 4; ++i) if (!std::isfinite(wb[i]) || wb[i] <= 0.0F) return false;
|
for (size_t i = 0; i < 4; ++i) if (!std::isfinite(wb[i]) || wb[i] <= 0.0F) return false;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -175,6 +177,8 @@ extern "C" Lardon3DRawDevelopmentResult lardon3d_raw_development_policy_fingerpr
|
||||||
return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
||||||
std::vector<unsigned char> bytes;
|
std::vector<unsigned char> bytes;
|
||||||
bytes.reserve(kRawFingerprintPayloadSize);
|
bytes.reserve(kRawFingerprintPayloadSize);
|
||||||
|
// Record all policy choices and relevant backend versions; decoder
|
||||||
|
// defaults must never create an untracked representation.
|
||||||
const char magic[] = "L3DRAWD1";
|
const char magic[] = "L3DRAWD1";
|
||||||
bytes.insert(bytes.end(), magic, magic + sizeof(magic) - 1);
|
bytes.insert(bytes.end(), magic, magic + sizeof(magic) - 1);
|
||||||
put_u32(&bytes, 1); put_u32(&bytes, kBackendKindLibRaw);
|
put_u32(&bytes, 1); put_u32(&bytes, kBackendKindLibRaw);
|
||||||
|
|
@ -247,6 +251,9 @@ extern "C" Lardon3DRawDevelopmentResult lardon3d_raw_develop_to_capture(
|
||||||
if (!verify_managed_asset(managed_source, output->source_asset.sha256)) return LARDON3D_RAW_DEVELOPMENT_SOURCE_CHANGED;
|
if (!verify_managed_asset(managed_source, output->source_asset.sha256)) return LARDON3D_RAW_DEVELOPMENT_SOURCE_CHANGED;
|
||||||
LibRaw raw;
|
LibRaw raw;
|
||||||
raw.imgdata.params.user_qual = 3; raw.imgdata.params.half_size = 0;
|
raw.imgdata.params.user_qual = 3; raw.imgdata.params.half_size = 0;
|
||||||
|
// The resolved WB is explicit. Automatic WB, brightness, and color
|
||||||
|
// choices are disabled so policy rather than hidden decoder state defines
|
||||||
|
// the derived asset.
|
||||||
raw.imgdata.params.user_flip = 0; raw.imgdata.params.use_auto_wb = 0;
|
raw.imgdata.params.user_flip = 0; raw.imgdata.params.use_auto_wb = 0;
|
||||||
raw.imgdata.params.use_camera_wb = 0; raw.imgdata.params.use_camera_matrix = 0;
|
raw.imgdata.params.use_camera_wb = 0; raw.imgdata.params.use_camera_matrix = 0;
|
||||||
raw.imgdata.params.output_profile = nullptr; raw.imgdata.params.camera_profile = nullptr;
|
raw.imgdata.params.output_profile = nullptr; raw.imgdata.params.camera_profile = nullptr;
|
||||||
|
|
|
||||||
10
src/task.c
10
src/task.c
|
|
@ -220,6 +220,9 @@ lardon3d_task_start(
|
||||||
const Lardon3DResourceReservation *reservation
|
const Lardon3DResourceReservation *reservation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
/* Start assumes admission/contract was already decided by the queue/ governor.
|
||||||
|
* Task only installs the provided active reservation and owns execution state.
|
||||||
|
*/
|
||||||
Lardon3DResourceReservationInfo information;
|
Lardon3DResourceReservationInfo information;
|
||||||
if (!task || !lardon3d_resource_reservation_get_active(
|
if (!task || !lardon3d_resource_reservation_get_active(
|
||||||
governor,
|
governor,
|
||||||
|
|
@ -285,6 +288,10 @@ lardon3d_task_start(
|
||||||
copy_text(task->message, sizeof(task->message), "Tâche en cours.");
|
copy_text(task->message, sizeof(task->message), "Tâche en cours.");
|
||||||
(void)pthread_mutex_unlock(&task->mutex);
|
(void)pthread_mutex_unlock(&task->mutex);
|
||||||
|
|
||||||
|
/* Callback is executed without task mutex held; caller-visible execution
|
||||||
|
* state transitions remain owned by task internals only.
|
||||||
|
* Finished callback is also issued after lock release.
|
||||||
|
*/
|
||||||
bool succeeded = task->callback(task, task->userdata);
|
bool succeeded = task->callback(task, task->userdata);
|
||||||
|
|
||||||
(void)pthread_mutex_lock(&task->mutex);
|
(void)pthread_mutex_lock(&task->mutex);
|
||||||
|
|
@ -563,6 +570,9 @@ lardon3d_task_sequence_break(
|
||||||
.gpu_slots = information.gpu_slots,
|
.gpu_slots = information.gpu_slots,
|
||||||
.io_slots = information.io_slots,
|
.io_slots = information.io_slots,
|
||||||
};
|
};
|
||||||
|
// sequence_break releases one active reservation and acquires the
|
||||||
|
// next one before returning; tasks should continue only from a
|
||||||
|
// cleanly admitted boundary.
|
||||||
task->has_contract = true;
|
task->has_contract = true;
|
||||||
++task->sequence_count;
|
++task->sequence_count;
|
||||||
*out_reservation = next;
|
*out_reservation = next;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ static const unsigned char checkpoint_magic[8] = {
|
||||||
'L', '3', 'D', 'T', 'A', 'S', 'K', '\0'
|
'L', '3', 'D', 'T', 'A', 'S', 'K', '\0'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Checkpoint v1 is fixed-size and field-by-field little-endian: magic,
|
||||||
|
* version, size, checksum, then generic runtime state. Task-kind payloads,
|
||||||
|
* callbacks, locks, and reservations are intentionally transient and absent. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
put_u32(unsigned char *output, uint32_t value)
|
put_u32(unsigned char *output, uint32_t value)
|
||||||
{
|
{
|
||||||
|
|
@ -288,6 +292,10 @@ lardon3d_task_checkpoint_save(
|
||||||
const Lardon3DTaskDurableSnapshot *snapshot
|
const Lardon3DTaskDurableSnapshot *snapshot
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
/* The temporary file is synced before rename, then its parent directory is
|
||||||
|
* synced. Before rename the prior checkpoint remains authoritative; after
|
||||||
|
* rename a directory-sync failure is reported as published-not-durable,
|
||||||
|
* never as a rollbackable write. */
|
||||||
if (!path || !path[0] || !valid_snapshot(snapshot)) {
|
if (!path || !path[0] || !valid_snapshot(snapshot)) {
|
||||||
return LARDON3D_TASK_CHECKPOINT_INVALID;
|
return LARDON3D_TASK_CHECKPOINT_INVALID;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ enum {
|
||||||
LARDON3D_PENDING_RESOURCE_WAIT_MILLISECONDS = 500,
|
LARDON3D_PENDING_RESOURCE_WAIT_MILLISECONDS = 500,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* The queue owns submission order and bounded backpressure only.
|
||||||
|
* It does not own resource policy; every admissible decision comes from the
|
||||||
|
* Governor via reserve/evaluate calls.
|
||||||
|
*/
|
||||||
struct Lardon3DTaskQueue {
|
struct Lardon3DTaskQueue {
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
pthread_cond_t not_empty;
|
pthread_cond_t not_empty;
|
||||||
|
|
@ -61,7 +65,9 @@ unlink_pending(Lardon3DTaskQueue *queue, TaskNode *previous, TaskNode *node)
|
||||||
(void)pthread_cond_signal(&queue->not_full);
|
(void)pthread_cond_signal(&queue->not_full);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parcourt la file d'attente et sélectionne la première tâche admissible.
|
/* Parcourt la file d'attente dans son ordre FIFO et sélectionne la première
|
||||||
|
* tâche admissible; une attente de ressources peut donc laisser passer une
|
||||||
|
* tâche antérieure sans lui faire perdre sa place dans la file.
|
||||||
* Les tâches terminales ou refusées sont retirées de la file d'attente.
|
* Les tâches terminales ou refusées sont retirées de la file d'attente.
|
||||||
* Une tâche en attente de ressources reste en file et sera réévaluée.
|
* Une tâche en attente de ressources reste en file et sera réévaluée.
|
||||||
* Retourne NULL si aucune tâche ne peut démarrer immédiatement. */
|
* Retourne NULL si aucune tâche ne peut démarrer immédiatement. */
|
||||||
|
|
@ -154,6 +160,10 @@ static void *
|
||||||
queue_worker(void *context)
|
queue_worker(void *context)
|
||||||
{
|
{
|
||||||
Lardon3DTaskQueue *queue = context;
|
Lardon3DTaskQueue *queue = context;
|
||||||
|
/* Single worker only; execution is serialized by design. Queue preserves
|
||||||
|
* pending FIFO order with adaptive dispatch/backpressure; Governor decides
|
||||||
|
* admission.
|
||||||
|
*/
|
||||||
for (;;) {
|
for (;;) {
|
||||||
(void)pthread_mutex_lock(&queue->mutex);
|
(void)pthread_mutex_lock(&queue->mutex);
|
||||||
while (!queue->stopping && !queue->pending_head) {
|
while (!queue->stopping && !queue->pending_head) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue