+ for (j = 0; j < num; j++)
+ deps[j] = -1;
+
+ for (j = 1; j < num_ops[i]; j++) {
+ struct depend *dep, *next;
+
+ list_for_each_safe(&op[i][j].pre, dep, next, pre_list) {
+ if (deps[dep->prereq.file]
+ >= (int)dep->prereq.op_num)
+ talloc_free(dep);
+ else
+ deps[dep->prereq.file]
+ = dep->prereq.op_num;
+ }
+ }
+ }
+}
+
+#if TRAVERSALS_TAKE_TRANSACTION_LOCK
+/* Force an order among the traversals, so they don't deadlock (as much) */
+static void make_traverse_depends(char *filename[],
+ struct op *op[], unsigned int num_ops[],
+ unsigned int num)
+{
+ unsigned int i, num_traversals = 0;
+ int j;
+ struct op_desc *desc;
+
+ /* Sort by which one runs first. */
+ int compare_traverse_desc(const void *_a, const void *_b)
+ {
+ const struct op_desc *da = _a, *db = _b;
+ const struct op *a = &op[da->file][da->op_num],
+ *b = &op[db->file][db->op_num];
+
+ if (a->seqnum != b->seqnum)
+ return a->seqnum - b->seqnum;
+
+ /* If they have same seqnum, it means one didn't make any
+ * changes. Thus sort by end in that case. */
+ return a[a->group_len].seqnum - b[b->group_len].seqnum;
+ }
+
+ desc = talloc_array(NULL, struct op_desc, 1);
+
+ /* Count them. */
+ for (i = 0; i < num; i++) {
+ for (j = 1; j < num_ops[i]; j++) {
+ /* Traverse start (ignore those in
+ * transactions; they're already covered by
+ * transaction dependencies). */
+ if (starts_traverse(&op[i][j])
+ && !in_transaction(op[i], j)) {
+ desc = talloc_realloc(NULL, desc,
+ struct op_desc,
+ num_traversals+1);
+ desc[num_traversals].file = i;
+ desc[num_traversals].op_num = j;
+ num_traversals++;
+ }
+ }
+ }
+ qsort(desc, num_traversals, sizeof(desc[0]), compare_traverse_desc);
+
+ for (i = 1; i < num_traversals; i++) {
+ const struct op *prev = &op[desc[i-1].file][desc[i-1].op_num];
+ const struct op *curr = &op[desc[i].file][desc[i].op_num];
+
+ /* Read traverses don't depend on each other (read lock). */
+ if (prev->type == OP_TDB_TRAVERSE_READ_START
+ && curr->type == OP_TDB_TRAVERSE_READ_START)
+ continue;
+
+ /* Only make dependency if it's clear. */
+ if (compare_traverse_desc(&desc[i], &desc[i-1])) {
+ /* i depends on end of traverse i-1. */
+ struct op_desc end = desc[i-1];
+ end.op_num += prev->group_len;
+ add_dependency(NULL, op, filename, &desc[i], &end);
+ }
+ }
+ talloc_free(desc);
+}
+
+static void set_nonblock(int fd)
+{
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK) != 0)
+ err(1, "Setting pipe nonblocking");
+}
+
+static bool handle_backoff(struct op *op[], int fd)
+{
+ struct op_desc desc;
+ bool handled = false;
+
+ /* Sloppy coding: we assume PIPEBUF never fills. */
+ while (read(fd, &desc, sizeof(desc)) != -1) {
+ unsigned int i;
+ handled = true;
+ for (i = desc.op_num; i > 0; i--) {
+ if (op[desc.file][i].type == OP_TDB_TRAVERSE) {
+ /* We insert a fake end here. */
+ op[desc.file][i].type
+ = OP_TDB_TRAVERSE_END_EARLY;
+ break;
+ } else if (starts_traverse(&op[desc.file][i])) {
+ unsigned int start = i;
+ struct op tmp = op[desc.file][i];
+ /* Move the ops outside traverse. */
+ memmove(&op[desc.file][i],
+ &op[desc.file][i+1],
+ (desc.op_num-i-1) * sizeof(op[0][0]));
+ op[desc.file][desc.op_num] = tmp;
+ while (op[desc.file][i].group_start == start) {
+ op[desc.file][i++].group_start
+ = desc.op_num;
+ }
+ break;
+ }
+ }
+ }
+ return handled;
+}
+
+#else /* !TRAVERSALS_TAKE_TRANSACTION_LOCK */
+static bool handle_backoff(struct op *op[], int fd)
+{
+ return false;
+}
+#endif
+
+static void derive_dependencies(struct tdb_context *tdb,
+ char *filename[],
+ struct op *op[], unsigned int num_ops[],
+ unsigned int num)
+{
+ struct keyinfo *hash;
+ unsigned int h, i;
+
+ /* Create hash table for faster key lookup. */
+ hash = hash_ops(op, num_ops, num);
+
+ /* Sort them by sequence number. */
+ sort_ops(tdb, hash, filename, op, num);
+
+ /* Create dependencies back to the last change, rather than
+ * creating false dependencies by naively making each one
+ * depend on the previous. This has two purposes: it makes
+ * later optimization simpler, and it also avoids deadlock with
+ * same sequence number ops inside traversals (if one
+ * traversal doesn't write anything, two ops can have the same
+ * sequence number yet we can create a traversal dependency
+ * the other way). */
+ for (h = 0; h < total_keys * 2; h++) {
+ int prev = -1;
+
+ if (hash[h].num_users < 2)
+ continue;
+
+ for (i = 0; i < hash[h].num_users; i++) {
+ if (changes_db(&hash[h].key, &op[hash[h].user[i].file]
+ [hash[h].user[i].op_num])) {
+ depend_on_previous(op, filename, num,
+ hash[h].user, i, prev);
+ prev = i;
+ } else if (prev >= 0)
+ add_dependency(hash, op, filename,
+ &hash[h].user[i],
+ &hash[h].user[prev]);
+ }
+ }
+
+#if TRAVERSALS_TAKE_TRANSACTION_LOCK
+ make_traverse_depends(filename, op, num_ops, num);
+#endif
+
+ optimize_dependencies(op, num_ops, num);
+}
+
+static struct timeval run_test(char *argv[],
+ unsigned int num_ops[],
+ unsigned int hashsize[],
+ unsigned int tdb_flags[],
+ unsigned int open_flags[],
+ struct op *op[],
+ int fds[2])
+{
+ unsigned int i;
+ struct timeval start, end, diff;
+ bool ok = true;
+
+ for (i = 0; argv[i+2]; i++) {