Re: config library




Guys, before you continue discussing your config api, keep in mind
that Tom Tromey has a pretty good design written, I would like to see
this:

Miguel

From: Tom Tromey <tromey@cygnus.com>
Date: 13 Oct 1998 23:26:17 -0600
Message-ID: <87n26z67eu.fsf@cygnus.com>
Lines: 134
X-Mailer: Gnus v5.5/Emacs 20.2
Sender: owner-gnome-hackers@nuclecu.unam.mx
Precedence: bulk

I've appended a rough draft of a new "gnome-config.h".

The basic idea here is that we add a new GtkObject to represent a
section.  This can either come directly from a file (e.g., the
.desktop files) or through some magical means (CORBA interface).

Commits are atomic.  User code can be run when a config option changes
"behind your back".  Maybe there should be separate signals to notify
you when an option "changed" (meaning that it changed remotely but
wasn't touched here) or is "conflicted" (meaning it changed both
remotely and locally and some decision is required)?

One idea not apparent from the header is that each config key has a
"base".  The base is basically the relative name to the corresponding
file.  E.g., if you have a file ~/.gnome/foo/bar/options, then the key
"foo/bar/options/a/b" will have a base "foo/bar/options".  You can
move around anywhere beneath the base using gnome_config_set_section;
".." is used to move up.  If you need to switch bases, make a new
config object.  There is no way to do a trans-base atomic commit.

I've changed it so that privacy is an attribute of an entire section
(base) and not a key.  I don't think this is a big deal.

I imagine I'll write a CORBA-based config server (where should it go?
gnome-core? gnome-libs?) with appropriate IDL for the non-file case.

I'm not entirely happy with this scheme.  However, I think it does
address all the requirements on the list I sent out the other night.
Suggestions welcome.  Questions too.

Tom


typedef struct _GnomeConfig GnomeConfig;
typedef struct _GnomeConfigClass GnomeConfigClass;

struct _GnomeConfig
{
	char *file;
	char *section;
	gboolean is_private;
	/* Add implementation here.  Probably a hash table.  */
};

struct _GnomeConfigClass
{
	/* Return TRUE if new value should be accepted, FALSE
	   otherwise.  */
	gboolean (* changed) (GnomeConfig *config, const char *path,
			      const char *new_value);
};

typedef enum
{
	GNOME_CONFIG_OK = 0,
	GNOME_CONFIG_NAME_CLASH,
	GNOME_CONFIG_IO_ERROR,
	GNOME_CONFIG_SECTION_ACCESS
} GnomeConfigError_t;

GnomeConfig *gnome_config_new (const char *path, gboolean priv);

GnomeConfig *gnome_config_new_from_file (const char *filename);

char *gnome_config_get_base (const GnomeConfig *config);

char *gnome_config_get_section (const GnomeConfig *config);

GnomeConfigError_t gnome_config_set_section (GnomeConfig *config,
					     const char *section);

GnomeConfigError_t gnome_config_set_string (GnomeConfig *config,
					    const char *key,
					    const char *value);
GnomeConfigError_t gnome_config_set_translated_string (GnomeConfig *config,
						       const char *key,
						       const char *value);
GnomeConfigError_t gnome_config_set_int (GnomeConfig *config,
					 const char *key,
					 int value);
GnomeConfigError_t gnome_config_set_float (GnomeConfig *config,
					   const char *key,
					   gdouble value);
GnomeConfigError_t gnome_config_set_bool (GnomeConfig *config,
					  const char *key,
					  gboolean value);
GnomeConfigError_t gnome_config_set_vector (GnomeConfig *config,
					    const char *key,
					    int argc,
					    const char *argv[]);

char *gnome_config_get_string_full (GnomeConfig *config,
				    const char *key,
				    const char *def,
				    gboolean *def_used);
char *gnome_config_get_translated_string_full (GnomeConfig *config,
					       const char *key,
					       const char *def,
					       gboolean *def_used);
int gnome_config_get_int_full (GnomeConfig *config,
			       const char *key,
			       int def,
			       gboolean *def_used);
gdouble gnome_config_get_float_full (GnomeConfig *config,
				     const char *key,
				     gdouble def,
				     gboolean *def_used);
gboolean gnome_config_get_bool_full (GnomeConfig *config,
				     const char *key,
				     gboolean def,
				     gboolean *def_used);

#define gnome_config_get_string(Config,Key) \
  gnome_config_get_string_full((Config), (Key), NULL, NULL)
#define gnome_config_get_translated_string(Config,Key) \
  gnome_config_get_translated_string_full((Config), (Key), NULL, NULL)
#define gnome_config_get_int(Config,Key) \
  gnome_config_get_int_full((Config), (Key), NULL, NULL)
#define gnome_config_get_float(Config,Key) \
  gnome_config_get_float_full((Config), (Key), NULL, NULL)
#define gnome_config_get_bool(Config,Key) \
  gnome_config_get_bool_full((Config), (Key), NULL, NULL)

/* No full version of this function.  */
void gnome_config_get_vector (GnomeConfig *config,
			      const char *key,
			      int *argc,
			      const char **argv[]);

char **gnome_config_list_keys (const GnomeConfig *config,
			       gboolean subkeys);

GnomeConfigError_t gnome_config_sync (const GnomeConfig *config);


Extra bits:

To: Gnome Hackers List <gnome-hackers@nuclecu.unam.mx>
Subject: Re: gnome-config 
In-reply-to: Your message of "13 Oct 1998 23:26:17 MDT."
             <87n26z67eu.fsf@cygnus.com> 
Date: Wed, 14 Oct 1998 02:53:50 EDT
From: Maciej Stachowiak <mstachow@MIT.EDU>
Sender: owner-gnome-hackers@nuclecu.unam.mx
Precedence: bulk


tromey@cygnus.com writes:
> I've appended a rough draft of a new "gnome-config.h".
> 
> The basic idea here is that we add a new GtkObject to represent a
> section.  This can either come directly from a file (e.g., the
> .desktop files) or through some magical means (CORBA interface).
> 
> Commits are atomic.  User code can be run when a config option changes
> "behind your back".  Maybe there should be separate signals to notify
> you when an option "changed" (meaning that it changed remotely but
> wasn't touched here) or is "conflicted" (meaning it changed both
> remotely and locally and some decision is required)?
> 

The possibility of conflicts is bad. How about requiring the app to
lock the section (which would impose some sort of global locking via
the back end) before changing it, and unlocking it when commiting or
canceling?

> One idea not apparent from the header is that each config key has a
> "base".  The base is basically the relative name to the corresponding
> file.  E.g., if you have a file ~/.gnome/foo/bar/options, then the key
> "foo/bar/options/a/b" will have a base "foo/bar/options".  You can
> move around anywhere beneath the base using gnome_config_set_section;
> ".." is used to move up.  If you need to switch bases, make a new
> config object.  There is no way to do a trans-base atomic commit.

I think it is bad that the API should make the difference visible this
way. Although for a separate-file-per-base back end it might make
sense, if you think about configuration space abstractly it is
completely illogical for a configuration key path to be split at one
point for semantic purposes, depending on implementation details.

> 
> I've changed it so that privacy is an attribute of an entire section
> (base) and not a key.  I don't think this is a big deal.
> 

Again I object to the entire "base" concept on aesthetic grounds.

> I imagine I'll write a CORBA-based config server (where should it go?
> gnome-core? gnome-libs?) with appropriate IDL for the non-file case.
> 

I think a config server should be used in all cases, regardless of the
back end. CORBA should be the sole mechanism for different back
ends. This is necessary anyway to implement proper notification, and
the locking necessary to avoid conflicts (conflicts are evil).

It should be spawned automatically if not already running to remove
concerns about Gnome apps running in a generally non-Gnome
environment.

> I'm not entirely happy with this scheme.  However, I think it does
> address all the requirements on the list I sent out the other night.
> Suggestions welcome.  Questions too.
> 

I think it exposes way too many details of the current file back end
and turns them into general API restrictions. 

If that level of backwards compatibility is going to so uglify the
API, then IMO backward compatibility should be broken, or provided
only by a migration script that converts the .gnome directory.

I propose an API more like the following, omiting the definitions of
the relevant structs because I am not sure what internal data they
will need to talk to the CORBA server properly. I think the
configuration namespace should be single-rooted, I do not see why this
would be a bad thing.

This proposed API is based on two main premises: 

1) The artificial distinction between filename and section name is bad.

2) Conflicts are bad, they should never happen. It is better that an
app should sometimes have to wait on a lock to avoid the very
possibility of conflict than that it should need to have code to clean
up after a conflict. If we assume conflicts will happen rarely
(probably true), then the locking should not impose much cost.


The lock referenced in the code below is per-section and is a
read-write lock, i.e. it has the rule that at any time there may be
EITHER an arbitrary number of readers OR exactly one writer with the
lock. Holding the lock for writing also allows you to read of course.


typedef enum
{
        GNOME_CONFIG_OK = 0,
	GNOME_CONFIG_LOCK_TIMEOUT,     /* Timeout trying to acquire a read or write lock. */ 
	GNOME_CONFIG_NO_SUBSECTION,    /* Tried to acquire handle on a subsection that doesn't exist. */
	GNOME_CONFIG_WRONG_TYPE        /* Tried to set a value key to a subsection or vice versa without first removing. */
	GNOME_CONFIG_NO_LOCK           /* Tried to abort or commit with a read lock, or unlock with a write lock, or do
					  either with no lock. */
} GnomeConfigError_t;

typedef enum
{
        GNOME_CONFIG_LOCK_READ,
	GNOME_CONFIG_LOCK_WRITE
} GnomeConfigLockMode;

/*
 * Getting a config handle.
 */

 /* Return a config handle to the specified section. */
GnomeConfigError_t gnome_config_new(char *path, g_long timeout, GnomeConfig **retval);

/* Get a handle to a subsection of a section you already have a handle
   on. */
GnomeConfigError_t gnome_config_subsection(GnomeConfig *config, g_long timeout, GnomeConfig **retval);

/* 
 * Acquiring the lock. 
 */

/* Lock the section's local keys only, not subsections. */
GnomeConfigError_t gnome_config_lock(GnomeConfig *config, GnomeConfigLockMode mode);

/* Lock the section and all subsections. */
GnomeConfigError_t gnome_config_lock_tree(GnomeConfig *config, GnomeConfigLockMode mode);


/* 
 * Getting and setting values.
 */

/* All gnome_config_set_ functions require this GnomeConfig object to
be holding the write lock. */

/* Set key to point to a subsection. Requires the key to not exist. */
GnomeConfigError_t gnome_config_set_subsection(GnomeConfig *config, 
						char *key);

/* Set the key specified to have no value; cannot be used on a subsection. Requires a write lock. */
GnomeConfigError_t gnome_config_remove_key(GnomeConfig *config, 
						char *key);


/* Remove the specified subsection. Requires a tree lock on the current section
   to ensure no one is trying to read or write that subsection. */
GnomeConfigError_t gnome_config_remove_section(GnomeConfig *config, 
						char *key);



/* Everything below as in Tom's API until further notice. */

GnomeConfigError_t gnome_config_set_string (GnomeConfig *config,
                                            const char *key,
                                            const char *value);
GnomeConfigError_t gnome_config_set_translated_string (GnomeConfig *config,
                                                       const char *key,
                                                       const char *value);
GnomeConfigError_t gnome_config_set_int (GnomeConfig *config,
                                         const char *key,
                                         int value);
GnomeConfigError_t gnome_config_set_float (GnomeConfig *config,
                                           const char *key,
                                           gdouble value);
GnomeConfigError_t gnome_config_set_bool (GnomeConfig *config,
                                          const char *key,
                                          gboolean value);
GnomeConfigError_t gnome_config_set_vector (GnomeConfig *config,
                                            const char *key,
                                            int argc,
                                            const char *argv[]);

/* All the gnome_config_get functions require a read lock or a write lock to be held. */

char *gnome_config_get_string_full (GnomeConfig *config,
                                    const char *key,
                                    const char *def,
                                    gboolean *def_used);
char *gnome_config_get_translated_string_full (GnomeConfig *config,
                                               const char *key,
                                               const char *def,
                                               gboolean *def_used);
int gnome_config_get_int_full (GnomeConfig *config,
                               const char *key,
                               int def,
                               gboolean *def_used);
gdouble gnome_config_get_float_full (GnomeConfig *config,
                                     const char *key,
                                     gdouble def,
                                     gboolean *def_used);
gboolean gnome_config_get_bool_full (GnomeConfig *config,
                                     const char *key,
                                     gboolean def,
                                     gboolean *def_used);

#define gnome_config_get_string(Config,Key) \
  gnome_config_get_string_full((Config), (Key), NULL, NULL)
#define gnome_config_get_translated_string(Config,Key) \
  gnome_config_get_translated_string_full((Config), (Key), NULL, NULL)
#define gnome_config_get_int(Config,Key) \
  gnome_config_get_int_full((Config), (Key), NULL, NULL)
#define gnome_config_get_float(Config,Key) \
  gnome_config_get_float_full((Config), (Key), NULL, NULL)
#define gnome_config_get_bool(Config,Key) \
  gnome_config_get_bool_full((Config), (Key), NULL, NULL)

/* No full version of this function.  */
void gnome_config_get_vector (GnomeConfig *config,
                              const char *key,
                              int *argc,
                              const char **argv[]);

char **gnome_config_list_keys (const GnomeConfig *config,
                               gboolean subkeys);

/*
 * Releasing the lock:
 */

/* Commit the changes made and release the write lock. */
GnomeConfigError_t gnome_config_commit (const GnomeConfig *config);

/* Do NOT commit the changes made, but DO release the write lock. */
GnomeConfigError_t gnome_config_abort (const GnomeConfig *config);

/* Release a read lock. */
GnomeConfigError_t gnome_config_unlock (const GnomeConfig *config);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]