]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/seqmod.c
be2efda0f108506a21b0f29ae8596de24f415c05
[openldap] / servers / slapd / overlays / seqmod.c
1 /* seqmod.c - sequenced modifies */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2004-2005 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* ACKNOWLEDGEMENTS:
16  * This work was initially developed by Howard Chu for inclusion in
17  * OpenLDAP Software.
18  */
19
20 #include "portable.h"
21
22 #ifdef SLAPD_OVER_SEQMOD
23
24 #include "slap.h"
25
26 /* This overlay serializes concurrent attempts to modify a single entry */
27
28 typedef struct modtarget {
29         struct modtarget *mt_next;
30         struct modtarget *mt_tail;
31         Operation *mt_op;
32 } modtarget;
33
34 typedef struct seqmod_info {
35         Avlnode         *sm_mods;       /* entries being modified */
36         ldap_pvt_thread_mutex_t sm_mutex;
37 } seqmod_info;
38
39 static int
40 sm_avl_cmp( const void *c1, const void *c2 )
41 {
42         const modtarget *m1, *m2;
43         int rc;
44
45         m1 = c1; m2 = c2;
46         rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;
47
48         if ( rc ) return rc;
49         return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
50 }
51
52 static int
53 seqmod_op_cleanup( Operation *op, SlapReply *rs )
54 {
55         slap_callback *sc = op->o_callback;
56         seqmod_info *sm = sc->sc_private;
57         modtarget *mt, mtdummy;
58         Avlnode  *av;
59
60         mtdummy.mt_op = op;
61         /* This op is done, remove it */
62         ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
63         av = avl_find2( sm->sm_mods, &mtdummy, sm_avl_cmp );
64         assert(av);
65
66         mt = av->avl_data;
67
68         /* If there are more, promote the next one */
69         if ( mt->mt_next ) {
70                 av->avl_data = mt->mt_next;
71                 mt->mt_next->mt_tail = mt->mt_tail;
72         } else {
73                 avl_delete( &sm->sm_mods, mt, sm_avl_cmp );
74         }
75         ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
76         op->o_callback = sc->sc_next;
77         op->o_tmpfree( sc, op->o_tmpmemctx );
78
79         return 0;
80 }
81
82 static int
83 seqmod_op_mod( Operation *op, SlapReply *rs )
84 {
85         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
86         seqmod_info             *sm = on->on_bi.bi_private;
87         modtarget       *mt;
88         Avlnode *av;
89         slap_callback *cb;
90
91         cb = op->o_tmpcalloc( 1, sizeof(slap_callback) + sizeof(modtarget),
92                 op->o_tmpmemctx );
93         mt = (modtarget *)(cb+1);
94         mt->mt_next = NULL;
95         mt->mt_tail = mt;
96         mt->mt_op = op;
97
98         /* See if we're already modifying this entry - don't allow
99          * near-simultaneous mods of the same entry
100          */
101         ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
102         av = avl_find2( sm->sm_mods, mt, sm_avl_cmp );
103         if ( av ) {
104                 modtarget *mtp = av->avl_data;
105                 mtp->mt_tail->mt_next = mt;
106                 mtp->mt_tail = mt;
107                 /* Wait for this op to get to head of list */
108                 while ( mtp != mt ) {
109                         ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
110                         ldap_pvt_thread_yield();
111                         /* Let it finish - should use a condition
112                          * variable here... */
113                         ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
114                         mtp = av->avl_data;
115                 }
116         } else {
117                 /* Record that we're modifying this now */
118                 avl_insert( &sm->sm_mods, mt, sm_avl_cmp, avl_dup_error );
119         }
120         ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
121
122         cb->sc_cleanup = seqmod_op_cleanup;
123         cb->sc_private = sm;
124         cb->sc_next = op->o_callback;
125         op->o_callback = cb;
126
127         return SLAP_CB_CONTINUE;
128 }
129
130 static int
131 seqmod_op_extended(
132         Operation *op,
133         SlapReply *rs
134 )
135 {
136         if ( exop_is_write( op )) return seqmod_op_mod( op, rs );
137         else return SLAP_CB_CONTINUE;
138 }
139
140 static int
141 seqmod_db_open(
142         BackendDB *be
143 )
144 {
145         slap_overinst   *on = (slap_overinst *)be->bd_info;
146         seqmod_info     *sm;
147
148         sm = ch_calloc(1, sizeof(seqmod_info));
149         on->on_bi.bi_private = sm;
150
151         ldap_pvt_thread_mutex_init( &sm->sm_mutex );
152
153         return 0;
154 }
155
156 static int
157 seqmod_db_close(
158         BackendDB *be
159 )
160 {
161         slap_overinst   *on = (slap_overinst *)be->bd_info;
162         seqmod_info     *sm = (seqmod_info *)on->on_bi.bi_private;
163
164         if ( sm ) {
165                 ldap_pvt_thread_mutex_destroy( &sm->sm_mutex );
166
167                 ch_free( sm );
168         }
169
170         return 0;
171 }
172
173 /* This overlay is set up for dynamic loading via moduleload. For static
174  * configuration, you'll need to arrange for the slap_overinst to be
175  * initialized and registered by some other function inside slapd.
176  */
177
178 static slap_overinst            seqmod;
179
180 int
181 seqmod_init()
182 {
183         seqmod.on_bi.bi_type = "seqmod";
184         seqmod.on_bi.bi_db_open = seqmod_db_open;
185         seqmod.on_bi.bi_db_close = seqmod_db_close;
186
187         seqmod.on_bi.bi_op_modify = seqmod_op_mod;
188         seqmod.on_bi.bi_op_modrdn = seqmod_op_mod;
189         seqmod.on_bi.bi_extended = seqmod_op_extended;
190
191         return overlay_register( &seqmod );
192 }
193
194 #if SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC
195 int
196 init_module( int argc, char *argv[] )
197 {
198         return seqmod_init();
199 }
200 #endif /* SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC */
201
202 #endif /* defined(SLAPD_OVER_SEQMOD) */