+static bool satisfies(const TDB_DATA *key, const TDB_DATA *data,
+ const struct op *op)
+{
+ const TDB_DATA *need = needs(key, op);
+
+ /* Don't need anything? Cool. */
+ if (!need)
+ return true;
+
+ /* This should be tdb_null or a real value. */
+ assert(data != &must_exist);
+ assert(data != &must_not_exist);
+ assert(data != ¬_exists_or_empty);
+
+ /* Must not exist? data must not exist. */
+ if (need == &must_not_exist)
+ return data == &tdb_null;
+
+ /* Must exist? */
+ if (need == &must_exist)
+ return data != &tdb_null;
+
+ /* Either noexist or empty. */
+ if (need == ¬_exists_or_empty)
+ return data->dsize == 0;
+
+ /* Needs something specific. */
+ return key_eq(*data, *need);
+}
+
+static void move_to_front(struct op_desc res[], unsigned off, unsigned elem)
+{
+ if (elem != off) {
+ struct op_desc tmp = res[elem];
+ memmove(res + off + 1, res + off, (elem - off)*sizeof(res[0]));
+ res[off] = tmp;
+ }
+}
+
+static void restore_to_pos(struct op_desc res[], unsigned off, unsigned elem)
+{
+ if (elem != off) {
+ struct op_desc tmp = res[off];
+ memmove(res + off, res + off + 1, (elem - off)*sizeof(res[0]));
+ res[elem] = tmp;
+ }
+}
+
+static bool sort_deps(char *filename[], struct op *op[],
+ struct op_desc res[],
+ unsigned off, unsigned num,
+ const TDB_DATA *key, const TDB_DATA *data,
+ unsigned num_files, unsigned fuzz)
+{
+ unsigned int i, files_done;
+ struct op *this_op;
+ bool done[num_files];
+
+ /* None left? We're sorted. */
+ if (off == num)
+ return true;
+
+ /* Does this make sequence number go backwards? Allow a little fuzz. */
+ if (off > 0) {
+ int seqnum1 = op[res[off-1].file][res[off-1].op_num].seqnum;
+ int seqnum2 = op[res[off].file][res[off].op_num].seqnum;
+
+ if (seqnum1 - seqnum2 > (int)fuzz) {
+#if DEBUG_DEPS
+ printf("Seqnum jump too far (%u -> %u)\n",
+ seqnum1, seqnum2);
+#endif
+ return false;
+ }
+ }
+
+ memset(done, 0, sizeof(done));
+
+ /* Since ops within a trace file are ordered, we just need to figure
+ * out which file to try next. Since we don't take into account
+ * inter-key relationships (which exist by virtue of trace file order),
+ * we minimize the chance of harm by trying to keep in seqnum order. */
+ for (files_done = 0, i = off; i < num && files_done < num_files; i++) {
+ if (done[res[i].file])
+ continue;
+
+ this_op = &op[res[i].file][res[i].op_num];
+
+ /* Is what we have good enough for this op? */
+ if (satisfies(key, data, this_op)) {
+ move_to_front(res, off, i);
+ if (sort_deps(filename, op, res, off+1, num,
+ key, gives(key, data, this_op),
+ num_files, fuzz))
+ return true;
+ restore_to_pos(res, off, i);
+ }
+ done[res[i].file] = true;
+ files_done++;
+ }
+
+ /* No combination worked. */
+ return false;
+}
+
+static void check_dep_sorting(struct op_desc user[], unsigned num_users,
+ unsigned num_files)
+{
+#if DEBUG_DEPS
+ unsigned int i;
+ unsigned minima[num_files];
+
+ memset(minima, 0, sizeof(minima));
+ for (i = 0; i < num_users; i++) {
+ assert(minima[user[i].file] < user[i].op_num);
+ minima[user[i].file] = user[i].op_num;
+ }
+#endif
+}
+
+/* All these ops happen on the same key. Which comes first?
+ *
+ * This can happen both because read ops or failed write ops don't
+ * change sequence number, and also due to race since we access the
+ * number unlocked (the race can cause less detectable ordering problems,
+ * in which case we'll deadlock and report: fix manually in that case).
+ */
+static bool figure_deps(char *filename[], struct op *op[],
+ const TDB_DATA *key, const TDB_DATA *data,
+ struct op_desc user[],
+ unsigned num_users, unsigned num_files)
+{
+ unsigned int fuzz;
+
+ /* We prefer to keep strict seqnum order if possible: it's the
+ * most likely. We get more lax if that fails. */
+ for (fuzz = 0; fuzz < 100; fuzz = (fuzz + 1)*2) {
+ if (sort_deps(filename, op, user, 0, num_users, key, data,
+ num_files, fuzz))
+ break;
+ }
+
+ if (fuzz >= 100)
+ return false;
+
+ check_dep_sorting(user, num_users, num_files);
+ return true;
+}
+
+/* We're having trouble sorting out dependencies for this key. Assume that it's
+ * a pre-existing record in the db, so determine a likely value. */
+static const TDB_DATA *preexisting_data(char *filename[], struct op *op[],
+ const TDB_DATA *key,
+ struct op_desc *user,
+ unsigned int num_users)
+{
+ unsigned int i;
+ const TDB_DATA *data;
+
+ for (i = 0; i < num_users; i++) {
+ data = needs(key, &op[user->file][user->op_num]);
+ if (data && data != &must_not_exist) {
+ if (!quiet)
+ printf("%s:%u: needs pre-existing record\n",
+ filename[user->file], user->op_num+1);
+ return data;
+ }
+ }
+ return &tdb_null;
+}
+
+static void sort_ops(struct tdb_context *tdb,
+ struct keyinfo hash[], char *filename[], struct op *op[],
+ unsigned int num)
+{
+ unsigned int h;
+
+ /* Gcc nexted function extension. How cool is this? */
+ int compare_seqnum(const void *_a, const void *_b)
+ {
+ const struct op_desc *a = _a, *b = _b;
+
+ /* First, maintain order within any trace file. */
+ if (a->file == b->file)
+ return a->op_num - b->op_num;
+
+ /* Otherwise, arrange by seqnum order. */
+ if (op[a->file][a->op_num].seqnum !=
+ op[b->file][b->op_num].seqnum)
+ return op[a->file][a->op_num].seqnum
+ - op[b->file][b->op_num].seqnum;
+
+ /* Cancelled transactions are assumed to happen first. */
+ if (starts_transaction(&op[a->file][a->op_num])
+ && !successful_transaction(&op[a->file][a->op_num]))
+ return -1;
+ if (starts_transaction(&op[b->file][b->op_num])
+ && !successful_transaction(&op[b->file][b->op_num]))
+ return 1;
+
+ /* No idea. */
+ return 0;
+ }
+
+ /* Now sort into seqnum order. */
+ for (h = 0; h < total_keys * 2; h++) {
+ struct op_desc *user = hash[h].user;
+
+ qsort(user, hash[h].num_users, sizeof(user[0]), compare_seqnum);
+ if (!figure_deps(filename, op, &hash[h].key, &tdb_null, user,
+ hash[h].num_users, num)) {
+ const TDB_DATA *data;
+
+ data = preexisting_data(filename, op, &hash[h].key,
+ user, hash[h].num_users);
+ /* Give the first op what it wants: does that help? */
+ if (!figure_deps(filename, op, &hash[h].key, data, user,
+ hash[h].num_users, num))
+ fail(filename[user[0].file], user[0].op_num+1,
+ "Could not resolve inter-dependencies");
+ if (tdb_store(tdb, hash[h].key, *data, TDB_INSERT) != 0)
+ errx(1, "Could not store initial value");
+ }
+ }
+}
+
+static int destroy_depend(struct depend *dep)
+{
+ list_del(&dep->pre_list);
+ list_del(&dep->post_list);
+ return 0;
+}
+