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