Существует два базовых c способа структурирования:
Предоставление монопольного доступа всей подсистеме к одному потоку за раз. Вы можете достичь этого с помощью мьютекса и условной переменной, которая может быть c членом foo()
, если хотите. Это невидимо для вызывающих, но предотвращает весь параллелизм, даже между foo()
вызовами в случае нескольких блоков.
Предоставьте каждому потоку свои собственные данные. Это то, что вам нужно, если вы не можете приспособиться к несовместимости foo()
.
Даже без доступа к встроенной поддержке локальных потоков данных C11 может случиться так, что какой-либо API потоков у вас есть механизм TLD или существенный эквивалент. В частности, pthreads делает , если это то, что вы используете. Возможно, вы даже можете свернуть свой собственный с помощью таблицы ha sh.
Но поскольку вы пишете для системы с ограниченными ресурсами, вы, вероятно, заинтересованы в облегченных подходах и абсолютных легких Весовой подход состоит в том, чтобы каждый поток предоставлял свои собственные данные, давая struct_t
a bar
для члена. Это не только исключает возможность косвенного обращения или поиска при доступе к данным для каждого потока, но также позволяет избежать динамического распределения памяти c. Но это действительно bar
для foo
вызывающих абонентов, что вы говорите, что вы не должны делать.
Между ними у вас есть альтернатива иметь foo
выделить bar
с, и передать их отключить непрозрачно для вызывающего абонента, ожидая, что они будут возвращаться при каждом связанном вызове. Это может лучше всего соответствовать вашим потребностям, и именно на этом я сосредоточусь до конца этого ответа.
Чтобы реализовать такой подход, сначала дайте struct_t
дополнительный элемент для хранения данных контекста:
struct struct_t {
char* data;
int dataLen;
bool finalBlock;
char checksum[CHECKSUM_SIZE];
void *state; // <-- this
};
Ожидается, что вызывающая сторона установит state
в нулевой указатель для начального вызова последовательности (то есть, как foo
может это распознать, чего я не вижу в других положениях) и при последующих вызовах передавать обратно любое значение foo
, указанное в нем. Таким образом, естественный шаблон вызова будет выглядеть примерно так:
struct struct_t checksum_state = { 0 };
int result;
checksum_state.data = some_data;
checksum_state.dataLen = the_length;
result = foo(&checksum_state);
// ... check / handle result ...
checksum_state.data = more_data;
checksum_state.dataLen = new_length;
result = foo(&checksum_state);
// ... check / handle result ...
checksum_state.data = last_data;
checksum_state.dataLen = last_length;
checksum_state.finalBlock = 1;
result = foo(&checksum_state);
// ... check / handle result ...
// ... use checksum_state.checksum ...
Я предполагаю, что это примерно то, что вы уже имеете для шаблона использования, и я отмечаю, что он не требует от вызывающего абонента явного использования или подтверждения checksum_state.state
вообще, хотя это выполняется частично вызывающей стороной, использующей инициализатор вместо инициализации для каждого члена.
На стороне foo()
это будет структурировано примерно так:
int foo(struct_t* x) {
int retCode = 0;
struct bar *y;
if (!x->state) {
x->state = calloc(1, sizeof(struct bar));
if (!x->state) // ... handle allocation error ...
}
y = x->state;
/* Do stuff with x and y */
if (x->lastBlock) {
free(x->state);
}
return retCode;
}