Stefan Fuhrmann wrote on Mon, Jul 04, 2011 at 23:55:28 +0200: > On 02.07.2011 04:24, Greg Stein wrote: > >On Fri, Jul 1, 2011 at 15:04,<stef...@apache.org> wrote: > >>+/** Release the @a mutex, previously acquired using @ref svn_mutex__lock > >>+ * that has been enabled in @ref svn_mutex__init. > >>+ */ > >>+svn_error_t * > >>+svn_mutex__unlock(svn_mutex__t mutex, > >>+ svn_error_t *err); > >>+ > >>+#ifdef __cplusplus > >>+} > >>+#endif /* __cplusplus */ > >I agree with Daniel's suggestion to add a "with_lock" function that > >invokes a callback with the mutex held. That is probably the safest > >way to use the mutexes, and it will always guarantee they are unlocked > >(eg. in the face of an error). I see that you're accepting an ERR as a > >parameter on the unlock, presumably to try and handle these error-exit > >situations. My preference would be to completely omit the explicit > >lock/unlock functions, and *only* have the with_lock concept. I think > >it makes for better/safer code. > > > I understand what you guys are trying to achieve but > there seems to be no way to do it in a meaningful way. > r1142193 is an implementation for that and might be > useful in some situations. > > In most cases, however, it will complicate things and > make the code less dependable. My preferred locking > style: > > static svn_error_t * > bar() > { > ... prepare parameters 1 to 4 ... > SVN_ERR(svn_mutex__lock(mutex)); > return svn_mutex__unlock(mutex, foo(param1, param2, param3, param4)); > } > > Now the same using *__with_lock: > > struct foo_params_t > { > T1 param1; > T2 param2; > T3 param3; > T4 param4; > }; > > static svn_error_t * > foo_void(void* baton) > { > struct foo_params_t * p = baton; > return foo(p->param1, p->param2, p->param3, p->param4); > } > > static svn_error_t * > bar() > { > struct foo_params_t params; > > ... prepare parameters 1 to 4 ... > > params.param1 = param1; > params.param2 = param2; > params.param3 = param3; > params.param4 = param4; > > return svn_mutex__with_lock(mutex, foo_void, ¶ms); > } > > Despite the obvious coding overhead, we also lose > type safety. So, I don't see an actual advantage in > a widespread use of the *__with_lock API. > > Instead, we should strife to refactor functions such > that they lock and unlock only in one place. Then we > can use the initial pattern.
I see your point. (The example I have in mind is the "svn_fs_fs__foo() { return svn_fs_fs__with_write_lock(foo_body, baton); }" functions.) I think it applies more generally: for example: the run_* functions in workqueue.c generally do two things --- parse the arguments out of a skel, and use those arguments. I think they would have been far easier to read if each run_foo() was split into a 'parse_foo_skel()' function (conforming to some library-private function signature) and a 'do_foo' function (which takes explicitly in its signature the parameters passed via the skel). eg, static svn_error_t * run_file_install(svn_wc__db_t *db, const svn_skel_t *work_item, const char *wri_abspath, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { const char *local_relpath = work_item->children->next; svn_boolean_t use_commit_times = work_item->children->next->next; svn_boolean_t record_fileinfo = work_item->children->next->next->next; const char *source_abspath = work_item->children->next->next->next->next; return do_postupgrade(db, wri_abspath, cancel_func, cancel_baton, scratch_pool, local_relpath, use_commit_times, record_fileinfo, NULL); }