GridLab
Grid Application Toolkit

A simple API for Grid Applications
GAT

Menu



Main Page   Alphabetical List   Compound List   File List   Compound Members   File Members  

GATObject.c

Go to the documentation of this file.
00001 /** @file GATObject.c.cpp
00002  * Source file for the GATObject class.
00003  *
00004  *  This file contains mostly generic implementations of the different 
00005  *  interfaces used throughout the GAT engine.
00006  *
00007  * @date Fri Oct 10 2003
00008  *
00009  * @version $Header: /export/cvs-gridlab/wp-1/Codes/GATEngine/C-reference/src/GATObject.c,v 1.34 2004/04/18 18:35:10 hartmutkaiser Exp $
00010  *
00011  *  Copyright (C) Hartmut Kaiser
00012  *  This file is part of the GAT Engine.
00013  *  Contributed by Hartmut Kaiser <hartmutkaiser [at] t-online [dot] de>.
00014  *
00015  *  Use, modification and distribution is subject to the Gridlab Software
00016  *  License. (See accompanying file GLlicense.txt or copy at
00017  *  http://www.gridlab.org/GLlicense.txt)
00018  */
00019  
00020 static const char *rcsid = "$Header: /export/cvs-gridlab/wp-1/Codes/GATEngine/C-reference/src/GATObject.c,v 1.34 2004/04/18 18:35:10 hartmutkaiser Exp $";
00021 
00022 /* System Header Files */
00023 #include "xds_p.h"
00024 
00025 /* GAT Header Files */
00026 #include "GAT.h"
00027 #include "GATInternal.h"
00028 #include "GATInterfaceMap.h"
00029 
00030 /* Macros */
00031 
00032 /* structs and typedefs */
00033 
00034 /* define the vtable types */
00035 GATOBJECT_DEFINE_VTABLE(GATObject);
00036 GATSERIALISABLE_DEFINE_VTABLE(GATObject);
00037 GATSTREAMABLE_DEFINE_VTABLE(GATObject);
00038 GATMONITORABLE_DEFINE_VTABLE(GATObject);
00039 GATRESOURCE_DEFINE_VTABLE(GATObject);
00040 
00041 /* the following data structures are used as a placeholder for the real object
00042    structures */
00043 struct GATObject_S {
00044   GATObject_vtable *__vtable;
00045 };
00046 
00047 struct GATSerialise_S {
00048   GATObject_ISerialisable_vtable *__vtable;
00049 };
00050 
00051 struct GATStreamable_S {
00052   GATObject_IStreamable_vtable *__vtable;
00053 };
00054 
00055 struct GATMonitorable_S {
00056   GATObject_IMonitorable_vtable *__vtable;
00057 };
00058 
00059 struct GATResource_S {
00060   GATObject_IResource_vtable *__vtable;
00061 };
00062 
00063 /* Static function prototypes */
00064 
00065 /* File scope variables */
00066 
00067 /* External functions */
00068 
00069 /** GATType GATObject_GetType(GATObject const object)
00070  *  @brief Get the type from a GATObject
00071  *
00072  *  The function GATObject_GetType implements a generic way to return the 
00073  *  actual type of a given GATObject.
00074  *
00075  *  @param object The GATObject, for which its type has to be returned
00076  *
00077  *  @return The @c #GATType of the object under inspection
00078  */
00079 GATType GATObject_GetType(GATObject_const object)
00080 {
00081   GATType retval = GATType_NoType;
00082   if (NULL != object)
00083   {
00084     retval = object->__vtable->get_type(object);
00085   }
00086   return retval;
00087 }
00088 
00089 /** void GATObject_Destroy(GATObject *object)
00090  *  @brief Destroy a GATObject
00091  *
00092  *  The function GATObject_Destroy implements a generic way to destroy an
00093  *  arbitrary GATObject.
00094  *
00095  *  @param object The GATObject, which has to be destroyed.
00096  */
00097 void GATObject_Destroy(GATObject *object)
00098 {
00099   if (NULL != object && NULL != *object)
00100     (*object)->__vtable->destroy(object);
00101 }
00102 
00103 /** int GATObject_Equals(GATObject const lhs, GATObject const rhs, GATBool *isequal)
00104  *  @brief Compare to GATObject's
00105  *
00106  *  The function GATObject_Equals allows to compare generically two arbitrary 
00107  *  GATObject's.
00108  *
00109  *  @param lhs The first GATObject to compare.
00110  *  @param rhs The second GATObject to compare.
00111  *  @param isequal The pointer, through which the result is to be returned.
00112  *
00113  *  @result An error code.
00114  */
00115 GATResult GATObject_Equals(GATObject_const lhs, GATObject_const rhs, 
00116   GATBool *isequal)
00117 {
00118   GATResult retval = GAT_INVALID_HANDLE;
00119   if (NULL != lhs && NULL != rhs)
00120   {
00121     if (lhs->__vtable->get_type(lhs) != rhs->__vtable->get_type(rhs))
00122     {
00123       if (NULL == isequal)
00124       {
00125         retval = GAT_INVALID_PARAMETER;
00126       }
00127       else
00128       {
00129         *isequal = GATFalse;
00130       }
00131     }
00132     else
00133     {
00134       retval = lhs->__vtable->equals(lhs, rhs, isequal);
00135     }
00136   }
00137   return retval;
00138 }
00139 
00140 /** int GATObject_Clone(GATObject const object, GATObject *new_object)
00141  *  @brief Clone a GATObject
00142  *
00143  *  The function GATObject_Clone allows to get a exact copy of an arbitrary
00144  *  GATObject. If the object to copy is a container object (i.e. GATTable, 
00145  *  GATList or similar), all the contained therein elements are copied too.
00146  *
00147  *  @param object The object to be copied.
00148  *  @param new_object The pointer, through which the result is to be returned.
00149  *
00150  *  @return An error type.
00151  */
00152 GATResult GATObject_Clone(GATObject_const object, GATObject *new_object)
00153 {
00154   GATResult retval = GAT_INVALID_HANDLE;
00155   if (NULL != object)
00156   {
00157     retval = object->__vtable->clone(object, new_object);
00158   }
00159   return retval;
00160 }
00161 
00162 /** int GATObject_GetInterface(GATObject_const object, GATInterface iftype, void const **ifp)
00163  *  @brief Get an interface supported by a GATObject
00164  *
00165  *  The function GATObject_GetInterface allows to get a pointer to an 
00166  *  additional interface supported by this GATObject.
00167  *
00168  *  @param object The object to be asked for the new interface.
00169  *  @param iftype The interface the object is to be asked for.
00170  *  @param ifp The pointer, through which the result is to be returned.
00171  *
00172  *  @return An error type.
00173  */
00174 GATResult GATObject_GetInterface(GATObject_const object, GATInterface iftype, 
00175   void const **ifp)
00176 {
00177   GATResult retval = GAT_INVALID_HANDLE;
00178   if (NULL != object)
00179   {
00180     if (NULL != object->__vtable->get_interface)
00181     {
00182       retval = object->__vtable->get_interface(object, iftype, ifp);
00183     }
00184     else
00185     {
00186       retval = GAT_NOTIMPL;
00187     }
00188   }
00189   return retval;
00190 }
00191 
00192 /** GATObject_GetCPIInstanceData
00193  *  @brief Get an the CPI instance dat associated with this CPI object.
00194  *
00195  *  The function GATObject_GetCPIInstanceData allows to get a pointer to the
00196  *  CPI instance data associated with the give object. Note, that this function
00197  *  returns meaningful result only for CPI objects.
00198  *
00199  *  @param object The object to be asked for the new interface.
00200  *  @param data The pointer to a variable, which should receive the reuslting 
00201  *        CPI instance data.
00202  *
00203  *  @return An error type.
00204  */
00205 GATResult 
00206 GATObject_GetCPIInstanceData(GATObject object, void **data)
00207 {
00208   GATResult retval = GAT_INVALID_HANDLE;
00209   if (NULL != object)
00210   {
00211     if (NULL != object->__vtable->get_instancedata)
00212     {
00213       retval = object->__vtable->get_instancedata(object, data);
00214     }
00215     else
00216     {
00217       retval = GAT_NOTIMPL;   // this is no CPI object
00218     }
00219   }
00220   return retval;
00221 }
00222 
00223 /* Helper functions needed for proper generic type conversion */
00224 GATObject GATObject_ToGATObject(GATObject object)
00225 {
00226   return object;
00227 }
00228 
00229 GATObject_const GATObject_ToGATObject_const(GATObject_const object)
00230 {
00231   return object;
00232 }
00233 
00234 /* Generic GATSerialise interface definition for GATObject's */
00235 
00236 /** int GATSerialisable_Serialise(GATObject object, GATObject stream, GATBool clear_dirty)
00237  *  @brief Serialise a GATObject object
00238  *
00239  *  The function GATSerialisable_Serialise serialises the given GATObject object into 
00240  *  the given stream in a generic way. 
00241  *
00242  *  @param object The GATObject object to serialise.
00243  *  @param stream The stream interface to use for the serialisation.
00244  *  @param clear_dirty If the clear_dirty parameter is set to GATTrue, the 
00245  *        internal dirty flag of this object is to be reset.
00246  *
00247  *  @return An error code.
00248  */
00249 GATResult 
00250 GATSerialisable_Serialise(GATObject object, GATObject stream, 
00251   GATBool clear_dirty)
00252 {
00253   GATSerialise serialise = NULL;
00254   GATResult retval = GATObject_GetInterface(object, GATInterface_ISerialisable, 
00255     (void const **) &serialise);
00256     
00257   if (GAT_SUCCESS == retval)
00258   {
00259     if (NULL != serialise->__vtable->serialise)
00260     {
00261       /* write the type of the object to the stream */
00262       GATuint32 type = GATObject_GetType(object);
00263       retval = GATuint32_Serialise(type, stream);
00264       if (GAT_SUCCESS == retval)
00265       {
00266         /* serialize the object itself */
00267         retval = serialise->__vtable->serialise(object, stream, clear_dirty);
00268       }
00269     }
00270     else 
00271     {
00272       retval = GAT_NOTIMPL;
00273     }
00274   }
00275   return retval;
00276 }
00277 
00278 /** GATObject GATSerialisable_DeSerialise(GATContext context, GATObject stream, GATBool clear_dirty)
00279  *  @brief De-serialise a GATObject object
00280  *
00281  *  The function GATSerialisable_DeSerialise de-serialises a streamed GATObject 
00282  *  object from the given stream in a generic way. It constructs a new instance 
00283  *  of the de-serialised object.
00284  *
00285  *  @param context The GAT context to be used for object construction.
00286  *  @param stream The stream interface to use for the serialisation.
00287  *  @param result The pointer to a variable, which receives the status code of
00288  *        the operation.
00289  *
00290  *  @return The newly constructed object.
00291  */
00292 GATObject 
00293 GATSerialisable_DeSerialise(GATContext context, GATObject stream, 
00294   GATResult *result)
00295 {
00296   /* read back the type of the GATObject to deserialise */
00297   GATObject new_object = NULL;
00298   GATuint32 type = GATType_NoType;
00299   GATResult retval = GATuint32_DeSerialise(stream, &type);
00300   
00301   if (GAT_SUCCESS == retval)
00302   {
00303     /* try to find the serialisation vtable of the given object type */
00304     GATObject_ISerialisable_vtable *vtable = NULL;
00305     
00306     retval = GATObject_Get_GATSerialisable((GATType)type, &vtable);
00307     if (GAT_SUCCESS == retval)
00308     {
00309       new_object = vtable->deserialise(context, stream, &retval);
00310     }
00311   }
00312   
00313   if (GAT_SUCCESS != retval)
00314   {
00315     GATObject_Destroy(&new_object);
00316   }
00317   
00318   if (NULL != result)
00319   {
00320     *result = retval;
00321   }
00322   return new_object;
00323 }
00324 
00325 /** int GATSerialisable_GetIsDirty(GATObject_const object, GATBool *isdirty)
00326  *  @brief Return the status of the dirty flag of an object
00327  *
00328  *  The function GATSerialisable_GetIsDirty retrieves the status of the dirty flag of
00329  *  an object in a generic way. 
00330  *
00331  *  @param object The GATObject object to ask for its dirty status.
00332  *  @param isdirty The pointer to a variable, which receives the dirty status.
00333  *
00334  *  @return An error code.
00335  */
00336 GATResult
00337 GATSerialisable_GetIsDirty(GATObject_const object, GATBool *isdirty)
00338 {
00339   GATSerialise serialise = NULL;
00340   GATResult retval = GATObject_GetInterface(object, GATInterface_ISerialisable, 
00341     (void const **) &serialise);
00342     
00343   if (GAT_SUCCESS == retval)
00344   {
00345     if (NULL != serialise->__vtable->isdirty)
00346     {
00347       /* ask the object itself */
00348       retval = serialise->__vtable->isdirty(object, isdirty);
00349     }
00350     else 
00351     {
00352       retval = GAT_NOTIMPL;
00353     }
00354   }
00355   return retval;
00356 }
00357 
00358 /* generic GATStreamable support */
00359 
00360 /** int GATStreamable_Read(GATObject object, void *buffer, GATuint32 size, GATuint32 *read_bytes)
00361  *  @brief Read a given amount of bytes from the given stream
00362  *
00363  *  The function GATStreamable_Read tries to read from the given stream the 
00364  *  given amount of data in a generic way.
00365  *
00366  *  @param object The object, which GATStreamable interface to use to access 
00367  *        the stream
00368  *  @param buffer The buffer to read the data to.
00369  *  @param size The size of the buffer (maximum amount of data to read).
00370  *  @param read_bytes The pointer to a variable, which receives the number of 
00371  *        bytes, actually returned in the buffer. This value is valid only, if
00372  *        no error occurs (optional, may be zero).
00373  *
00374  *  @return An error code.
00375  */
00376 GATResult 
00377 GATStreamable_Read(GATObject object, void *buffer, GATuint32 size, 
00378   GATuint32 *read_bytes)
00379 {
00380   GATStreamable stream = NULL;
00381   GATResult retval = GATObject_GetInterface(object, GATInterface_IStreamable, 
00382     (void const **) &stream);
00383   if (GAT_SUCCESS == retval)
00384   {
00385     if (NULL != stream->__vtable->sread)
00386     {
00387       /* delegate to the object itself */
00388       retval = stream->__vtable->sread(object, buffer, size, read_bytes);
00389     }
00390     else
00391     {
00392       retval = GAT_NOTIMPL;
00393     }
00394   }
00395   return retval;
00396 }
00397 
00398 /** int GATStreamable_Write(GATObject object, void const *buffer, GATuint32 size, GATuint32 *written_bytes)
00399  *  @brief Writes a given amount of bytes to the given stream
00400  *
00401  *  The function GATStreamable_Write tries to write to the given stream the 
00402  *  given amount of data in a generic way.
00403  *
00404  *  @param object The object, which GATStreamable interface to use to access 
00405  *        the stream
00406  *  @param buffer The buffer containing the data to write to the stream.
00407  *  @param size The size of the buffer (amount of data to write).
00408  *  @param written_bytes The pointer to a variable, which receives the number 
00409  *        of bytes actually written. This value is valid only, if
00410  *        no error occurs (optional, may be zero).
00411  *
00412  *  @return An error code.
00413  */
00414 GATResult
00415 GATStreamable_Write(GATObject object, void const *buffer, GATuint32 size, 
00416   GATuint32 *written_bytes)
00417 {
00418   GATStreamable stream = NULL;
00419   GATResult retval = GATObject_GetInterface(object, GATInterface_IStreamable, 
00420     (void const **) &stream);
00421   if (GAT_SUCCESS == retval)
00422   {
00423     if (NULL != stream->__vtable->swrite)
00424     {
00425       /* delegate to the object itself */
00426       retval = stream->__vtable->swrite(object, buffer, size, written_bytes);
00427     }
00428     else
00429     {
00430       retval = GAT_NOTIMPL;
00431     }
00432   }
00433   return retval;
00434 }
00435 
00436 /** int GATStreamable_Seek(GATObject object, GATOrigin origin, GATint32 offset, GATuint32 *new_position)
00437  *  @brief Writes a given amount of bytes to the given stream
00438  *
00439  *  The function GATStreamable_Write tries to write to the given stream the 
00440  *  given amount of data in a generic way.
00441  *
00442  *  @param object The object, which GATStreamable interface to use to access 
00443  *        the stream
00444  *  @param origin The point inside the stream, relative to which the pointer 
00445  *        positioning is to be done.
00446  *  @param offset The amount of bytes the logical position inside the stream 
00447  *        is to be moved.
00448  *  @param new_position The pointer to a variable, which receives the new 
00449  *        logical position inside the stream object (optional, may be zero).
00450  *
00451  *  @return An error code.
00452  */
00453 GATResult 
00454 GATStreamable_Seek(GATObject object, GATOrigin origin, GATint32 offset, 
00455   GATuint32 *new_position)
00456 {
00457   GATStreamable stream = NULL;
00458   GATResult retval = GATObject_GetInterface(object, GATInterface_IStreamable, 
00459     (void const **) &stream);
00460   if (GAT_SUCCESS == retval)
00461   {
00462     if (NULL != stream->__vtable->seek)
00463     {
00464       /* delegate to the object itself */
00465       retval = stream->__vtable->seek(object, origin, offset, new_position);
00466     }
00467     else
00468     {
00469       retval = GAT_NOTIMPL;
00470     }
00471   }
00472   return retval;
00473 }
00474 
00475 /* generic GATMonitorable interface */
00476 
00477 GATResult 
00478 GATMonitorable_AddMetricListener(GATObject object, 
00479   GATMetricListener listener, void *listener_data, GATMetric metric,
00480   GATuint32 *cookie)
00481 {
00482   GATMonitorable monitorable = NULL;
00483   GATResult retval = GATObject_GetInterface(object, GATInterface_IMonitorable, 
00484     (void const **) &monitorable);
00485   if (GAT_SUCCESS == retval)
00486   {
00487     if (NULL != monitorable->__vtable->addlistener)
00488     {
00489       /* delegate to the object itself */
00490       retval = monitorable->__vtable->addlistener(object, listener, 
00491         listener_data, metric, cookie);
00492     }
00493     else
00494     {
00495       retval = GAT_NOTIMPL;
00496     }
00497   }
00498   return retval;
00499 }
00500 
00501 GATResult 
00502 GATMonitorable_RegisterPolling(GATObject object, 
00503   GATMetric metric, GATMetricEvent *event, GATuint32 *cookie)
00504 {
00505   GATMonitorable monitorable = NULL;
00506   GATResult retval = GATObject_GetInterface(object, GATInterface_IMonitorable, 
00507     (void const **) &monitorable);
00508   if (GAT_SUCCESS == retval)
00509   {
00510     if (NULL != monitorable->__vtable->registerpolling)
00511     {
00512       /* delegate to the object itself */
00513       retval = monitorable->__vtable->registerpolling(object, metric, event, 
00514         cookie);
00515     }
00516     else
00517     {
00518       retval = GAT_NOTIMPL;
00519     }
00520   }
00521   return retval;
00522 }
00523 
00524 GATResult 
00525 GATMonitorable_RemoveRegisteredMetric(GATObject object, 
00526   GATMetric metric, GATuint32 cookie)
00527 {
00528   GATMonitorable monitorable = NULL;
00529   GATResult retval = GATObject_GetInterface(object, GATInterface_IMonitorable, 
00530     (void const **) &monitorable);
00531   if (GAT_SUCCESS == retval)
00532   {
00533     if (NULL != monitorable->__vtable->removemetric)
00534     {
00535       /* delegate to the object itself */
00536       retval = monitorable->__vtable->removemetric(object, metric, cookie);
00537     }
00538     else
00539     {
00540       retval = GAT_NOTIMPL;
00541     }
00542   }
00543   return retval;
00544 }
00545 
00546 GATResult 
00547 GATMonitorable_GetMetrics(GATObject_const object, GATList_GATMetric *metrics)
00548 {
00549   GATMonitorable monitorable = NULL;
00550   GATResult retval = GATObject_GetInterface(object, GATInterface_IMonitorable, 
00551     (void const **) &monitorable);
00552   if (GAT_SUCCESS == retval)
00553   {
00554     if (NULL != monitorable->__vtable->getmetrics)
00555     {
00556       /* delegate to the object itself */
00557       retval = monitorable->__vtable->getmetrics(object, metrics);
00558     }
00559     else
00560     {
00561       retval = GAT_NOTIMPL;
00562     }
00563   }
00564   return retval;
00565 }
00566 
00567 
00568 /* generic GATResource access support */
00569 GATResult
00570 GATResource_GetResourceDescription(GATObject_const object, 
00571   GATResourceDescription_const *description)
00572 {
00573   GATResource resource = NULL;
00574   GATResult retval = GATObject_GetInterface(object, GATInterface_IResource, 
00575     (void const **) &resource);
00576   if (GAT_SUCCESS == retval)
00577   {
00578     if (NULL != resource->__vtable->getresourcedescription)
00579     {
00580       /* delegate to the object itself */
00581       retval = resource->__vtable->getresourcedescription(object, description);
00582     }
00583     else
00584     {
00585       retval = GAT_NOTIMPL;
00586     }
00587   }
00588   return retval;
00589 }
00590 
00591 GATResult
00592 GATResource_GetReservation(GATObject_const object, 
00593   GATReservation_const *reservation)
00594 {
00595   GATResource resource = NULL;
00596   GATResult retval = GATObject_GetInterface(object, GATInterface_IResource, 
00597     (void const **) &resource);
00598   if (GAT_SUCCESS == retval)
00599   {
00600     if (NULL != resource->__vtable->getreservation)
00601     {
00602       /* delegate to the object itself */
00603       retval = resource->__vtable->getreservation(object, reservation);
00604     }
00605     else
00606     {
00607       retval = GAT_NOTIMPL;
00608     }
00609   }
00610   return retval;
00611 }  
00612 
00613 /* Serialize primitive data items */
00614 
00615 /** int GATuint32_Serialise(GATuint32 data, GATObject stream)
00616  *
00617  *  The GATuint32_Serialise function serialises a GATuint32 value into the 
00618  *  given stream.
00619  *
00620  *  @param data The GATuint32 data item to serialise (write).
00621  *  @param stream The stream to use for serialisation.
00622  *
00623  *  @return An error code.
00624  */
00625 GATResult
00626 GATuint32_Serialise(GATuint32 data, GATObject stream)
00627 {
00628   GATResult retval = GAT_FAIL;
00629   xds_t *xds = NULL;
00630   void *xds_buffer = NULL;
00631   GATuint32 xds_buffer_size = 0;
00632   
00633   if (XDS_OK == (retval = xds_init(&xds, XDS_ENCODE)) &&
00634       XDS_OK == (retval = xds_register(xds, "uint32", &xml_encode_uint32, NULL)) &&
00635       XDS_OK == (retval = xds_encode(xds, "uint32", data)) &&
00636       XDS_OK == (retval = xds_getbuffer(xds, XDS_GIFT, &xds_buffer, (size_t *) &xds_buffer_size)))
00637   {
00638       assert(NULL != xds_buffer);
00639       retval = GATStreamable_Write(stream, xds_buffer, xds_buffer_size, 0);
00640   }
00641   else
00642   {
00643     retval = XDS_TO_GAT(retval);
00644   }
00645 
00646   xds_destroy(xds);
00647   free(xds_buffer);
00648   return retval;
00649 }
00650 
00651 /** int GATuint32_DeSerialize(GATuint32 data, GATObject stream)
00652  *
00653  *  The GATuint32_DeSerialize function de-serialises a GATuint32 value into the 
00654  *  given stream.
00655  *
00656  *  @param stream The stream to use for serialisation.
00657  *  @param data The GATuint32 data item to de-serialise (read).
00658  *
00659  *  @return An error code.
00660  */
00661 GATResult
00662 GATuint32_DeSerialise(GATObject stream, GATuint32 *data)
00663 {
00664   char buffer[64];    /* seems to be sufficient for a GATuint32 value */
00665   GATuint32 xds_read_bytes = 0;
00666   GATResult retval = GATStreamable_Read(stream, buffer, sizeof(buffer), 
00667     &xds_read_bytes);
00668 
00669   if (GAT_SUCCESS == retval)
00670   {
00671     xds_t *xds = NULL;
00672     
00673     if (XDS_OK == (retval = xds_init      (&xds, XDS_DECODE)) &&
00674         XDS_OK == (retval = xds_register  ( xds, "uint32", &xml_decode_uint32, NULL)) &&
00675         XDS_OK == (retval = xds_setbuffer ( xds, XDS_LOAN, buffer, sizeof(buffer)))   &&
00676         XDS_OK == (retval = xds_decode    ( xds, "uint32", data)))
00677     {
00678       retval = GATStreamable_Seek (stream, GATOrigin_Current, 
00679         xds->buffer_len - xds_read_bytes, 0); 
00680     }
00681     else
00682     {
00683       retval = XDS_TO_GAT(retval);
00684     }
00685     xds_destroy(xds);
00686   }
00687   return retval;
00688 }
00689 
00690 /* local functions */