+ error = for_each_txn_row(txn, update_row_ref_count);
+ if (error) {
+ return error;
+ }
+
+ return for_each_txn_row(txn, check_ref_count);
+}
+
+static struct ovsdb_error *
+ovsdb_txn_row_commit(struct ovsdb_txn *txn OVS_UNUSED,
+ struct ovsdb_txn_row *txn_row)
+{
+ size_t n_indexes = txn_row->table->schema->n_indexes;
+
+ if (txn_row->old) {
+ size_t i;
+
+ for (i = 0; i < n_indexes; i++) {
+ struct hmap_node *node = ovsdb_row_get_index_node(txn_row->old, i);
+ hmap_remove(&txn_row->table->indexes[i], node);
+ }
+ }
+ if (txn_row->new) {
+ size_t i;
+
+ for (i = 0; i < n_indexes; i++) {
+ struct hmap_node *node = ovsdb_row_get_index_node(txn_row->new, i);
+ hmap_insert(&txn_row->table->indexes[i], node, node->hash);
+ }
+ }
+
+ ovsdb_txn_row_prefree(txn_row);
+ if (txn_row->new) {
+ txn_row->new->n_refs = txn_row->n_refs;
+ }
+ ovsdb_row_destroy(txn_row->old);
+ free(txn_row);
+
+ return NULL;
+}
+
+static void
+add_weak_ref(struct ovsdb_txn *txn,
+ const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
+{
+ struct ovsdb_row *src = CONST_CAST(struct ovsdb_row *, src_);
+ struct ovsdb_row *dst = CONST_CAST(struct ovsdb_row *, dst_);
+ struct ovsdb_weak_ref *weak;
+
+ if (src == dst) {
+ return;
+ }
+
+ dst = ovsdb_txn_row_modify(txn, dst);
+
+ if (!list_is_empty(&dst->dst_refs)) {
+ /* Omit duplicates. */
+ weak = CONTAINER_OF(list_back(&dst->dst_refs),
+ struct ovsdb_weak_ref, dst_node);
+ if (weak->src == src) {
+ return;
+ }
+ }
+
+ weak = xmalloc(sizeof *weak);
+ weak->src = src;
+ list_push_back(&dst->dst_refs, &weak->dst_node);
+ list_push_back(&src->src_refs, &weak->src_node);
+}
+
+static struct ovsdb_error * WARN_UNUSED_RESULT
+assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
+{
+ struct ovsdb_table *table;
+ struct shash_node *node;
+
+ if (txn_row->old) {
+ /* Mark rows that have weak references to 'txn_row' as modified, so
+ * that their weak references will get reassessed. */
+ struct ovsdb_weak_ref *weak, *next;
+
+ LIST_FOR_EACH_SAFE (weak, next, dst_node, &txn_row->old->dst_refs) {
+ if (!weak->src->txn_row) {
+ ovsdb_txn_row_modify(txn, weak->src);
+ }
+ }
+ }
+
+ if (!txn_row->new) {
+ /* We don't have to do anything about references that originate at
+ * 'txn_row', because ovsdb_row_destroy() will remove those weak
+ * references. */
+ return NULL;
+ }