2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 % DDDD IIIII SSSSS TTTTT RRRR IIIII BBBB U U TTTTT EEEEE %
6 % D D I SS T R R I B B U U T E %
7 % D D I SSS T RRRR I BBBB U U T EEE %
8 % D D I SS T R R I B B U U T E %
9 % DDDDA IIIII SSSSS T R R IIIII BBBB UUU T EEEEE %
11 % CCCC AAA CCCC H H EEEEE %
13 % C AAAAA C HHHHH EEE %
15 % CCCC A A CCCC H H EEEEE %
18 % MagickCore Distributed Pixel Cache Methods %
25 % Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
26 % dedicated to making software imaging solutions freely available. %
28 % You may not use this file except in compliance with the License. You may %
29 % obtain a copy of the License at %
31 % http://www.imagemagick.org/script/license.php %
33 % Unless required by applicable law or agreed to in writing, software %
34 % distributed under the License is distributed on an "AS IS" BASIS, %
35 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
36 % See the License for the specific language governing permissions and %
37 % limitations under the License. %
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 % A distributed pixel cache is an extension of the traditional pixel cache
42 % available on a single host. The distributed pixel cache may span multiple
43 % servers so that it can grow in size and transactional capacity to support
44 % very large images. Start up the pixel cache server on one or more machines.
45 % When you read or operate on an image and the local pixel cache resources are
46 % exhausted, ImageMagick contacts one or more of these remote pixel servers to
47 % store or retrieve pixels.
54 #include "MagickCore/studio.h"
55 #include "MagickCore/cache.h"
56 #include "MagickCore/cache-private.h"
57 #include "MagickCore/distribute-cache.h"
58 #include "MagickCore/distribute-cache-private.h"
59 #include "MagickCore/exception.h"
60 #include "MagickCore/exception-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/locale_.h"
64 #include "MagickCore/memory_.h"
65 #include "MagickCore/policy.h"
66 #include "MagickCore/random_.h"
67 #include "MagickCore/registry.h"
68 #include "MagickCore/string_.h"
69 #include "MagickCore/string-private.h"
70 #if defined(MAGICKCORE_HAVE_SOCKET)
71 #include <netinet/in.h>
73 #include <sys/socket.h>
74 #include <arpa/inet.h>
80 #define DPCHostname "127.0.0.1"
82 #define DPCSessionKeyLength 8
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 % A c q u i r e D i s t r i b u t e C a c h e I n f o %
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 % AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
97 % The format of the AcquireDistributeCacheInfo method is:
99 % DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
101 % A description of each parameter follows:
103 % o exception: return any errors or warnings in this structure.
107 static MagickSizeType CRC64(const unsigned char *message,const size_t length)
115 static MagickBooleanType
116 crc_initial = MagickFalse;
118 static MagickSizeType
121 if (crc_initial == MagickFalse)
126 for (i=0; i < 256; i++)
131 alpha=(MagickSizeType) i;
132 for (j=0; j < 8; j++)
134 if ((alpha & 0x01) == 0)
137 alpha=(MagickSizeType) ((alpha >> 1) ^
138 MagickULLConstant(0xd800000000000000));
142 crc_initial=MagickTrue;
145 for (i=0; i < (ssize_t) length; i++)
146 crc=crc_xor[(crc ^ message[i]) & 0xff] ^ (crc >> 8);
150 static int ConnectPixelCacheServer(const char *hostname,const int port,
151 MagickSizeType *session_key,ExceptionInfo *exception)
153 #if defined(MAGICKCORE_HAVE_SOCKET)
155 secret[MaxTextExtent];
174 session[MaxTextExtent];
177 Connect to distributed pixel cache and get session key.
180 shared_secret=GetPolicyValue("shared-secret");
181 if (shared_secret == (const char *) NULL)
183 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
184 "DistributedPixelCache","'%s'","shared secret expected");
187 (void) CopyMagickString((char *) session,shared_secret,MaxTextExtent-
188 DPCSessionKeyLength);
189 host=gethostbyname(hostname);
190 client_socket=socket(AF_INET,SOCK_STREAM,0);
191 if (client_socket == -1)
193 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
194 "DistributedPixelCache","'%s'",hostname);
197 (void) ResetMagickMemory(&address,0,sizeof(address));
198 address.sin_family=AF_INET;
199 address.sin_port=htons((uint16_t) port);
200 address.sin_addr=(*((struct in_addr *) host->h_addr));
201 status=connect(client_socket,(struct sockaddr *) &address,(socklen_t)
202 sizeof(struct sockaddr));
205 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
206 "DistributedPixelCache","'%s'",hostname);
209 count=read(client_socket,secret,MaxTextExtent);
212 (void) memcpy(session+strlen(shared_secret),secret,(size_t) count);
213 *session_key=CRC64(session,strlen(shared_secret)+count);
215 if (*session_key == 0)
217 close(client_socket);
220 return(client_socket);
222 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
223 "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
228 static char *GetHostname(int *port,ExceptionInfo *exception)
245 Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
247 hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",
249 if (hosts == (char *) NULL)
252 return(AcquireString(DPCHostname));
254 (void) SubstituteString(&hosts,","," ");
255 hostlist=StringToArgv(hosts,&argc);
256 hosts=DestroyString(hosts);
257 if (hostlist == (char **) NULL)
260 return(AcquireString(DPCHostname));
262 hosts=AcquireString(hostlist[(id++ % (argc-1))+1]);
263 for (i=0; i < (ssize_t) argc; i++)
264 hostlist[i]=DestroyString(hostlist[i]);
265 hostlist=(char **) RelinquishMagickMemory(hostlist);
266 (void) SubstituteString(&hosts,":"," ");
267 hostlist=StringToArgv(hosts,&argc);
268 if (hostlist == (char **) NULL)
271 return(AcquireString(DPCHostname));
273 host=AcquireString(hostlist[1]);
274 if (hostlist[2] == (char *) NULL)
277 *port=StringToLong(hostlist[2]);
278 for (i=0; i < (ssize_t) argc; i++)
279 hostlist[i]=DestroyString(hostlist[i]);
280 hostlist=(char **) RelinquishMagickMemory(hostlist);
284 MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
285 ExceptionInfo *exception)
291 *distribute_cache_info;
296 distribute_cache_info=(DistributeCacheInfo *) AcquireMagickMemory(
297 sizeof(*distribute_cache_info));
298 if (distribute_cache_info == (DistributeCacheInfo *) NULL)
299 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
300 (void) ResetMagickMemory(distribute_cache_info,0,
301 sizeof(*distribute_cache_info));
302 distribute_cache_info->signature=MagickSignature;
304 Contact pixel cache server.
306 distribute_cache_info->port=0;
307 hostname=GetHostname(&distribute_cache_info->port,exception);
309 distribute_cache_info->file=ConnectPixelCacheServer(hostname,
310 distribute_cache_info->port,&session_key,exception);
311 distribute_cache_info->session_key=session_key;
312 (void) CopyMagickString(distribute_cache_info->hostname,hostname,
314 hostname=DestroyString(hostname);
315 if (distribute_cache_info->file == -1)
316 distribute_cache_info=DestroyDistributeCacheInfo(distribute_cache_info);
317 return(distribute_cache_info);
321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
325 % D e s t r o y D i s t r i b u t e C a c h e I n f o %
329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
331 % DestroyDistributeCacheInfo() deallocates memory associated with an
332 % DistributeCacheInfo structure.
334 % The format of the DestroyDistributeCacheInfo method is:
336 % DistributeCacheInfo *DestroyDistributeCacheInfo(
337 % DistributeCacheInfo *distribute_cache_info)
339 % A description of each parameter follows:
341 % o distribute_cache_info: the distributed cache info.
344 MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
345 DistributeCacheInfo *distribute_cache_info)
347 assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
348 assert(distribute_cache_info->signature == MagickSignature);
349 distribute_cache_info->signature=(~MagickSignature);
350 distribute_cache_info=(DistributeCacheInfo *) RelinquishMagickMemory(
351 distribute_cache_info);
352 return(distribute_cache_info);
356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360 + D i s t r i b u t e P i x e l C a c h e S e r v e r %
364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366 % DistributePixelCacheServer() waits on the specified port for commands to
367 % create, read, update, or destroy a pixel cache.
369 % The format of the DistributePixelCacheServer() method is:
371 % void DistributePixelCacheServer(const size_t port)
373 % A description of each parameter follows:
375 % o port: connect the distributed pixel cache at this port.
377 % o exception: return any errors or warnings in this structure.
381 static MagickBooleanType CreateDistributeCache(int file,
382 const MagickSizeType session_key)
399 exception=AcquireExceptionInfo();
400 image=AcquireImage((ImageInfo *) NULL,exception);
401 count=read(file,&image->columns,sizeof(image->columns));
402 if (count != (ssize_t) sizeof(image->columns))
404 count=read(file,&image->rows,sizeof(image->rows));
405 if (count != (ssize_t) sizeof(image->rows))
407 count=read(file,&image->number_channels,sizeof(image->number_channels));
408 if (count != (ssize_t) sizeof(image->number_channels))
410 (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
411 status=SetImageRegistry(ImageRegistryType,key,image,exception);
412 exception=DestroyExceptionInfo(exception);
413 image=DestroyImage(image);
417 static MagickBooleanType DestroyDistributeCache(int file,
418 const MagickSizeType session_key)
423 (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
424 return(DeleteImageRegistry(key));
427 static MagickBooleanType ReadDistributeCache(int file,
428 const MagickSizeType session_key)
445 register const Quantum
454 exception=AcquireExceptionInfo();
455 (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
456 image=(Image *) GetImageRegistry(ImageRegistryType,key,exception);
457 exception=DestroyExceptionInfo(exception);
458 if (image == (Image *) NULL)
460 cache_info=(CacheInfo *) image->cache;
461 count=read(file,®ion.width,sizeof(region.width));
462 if (count != (ssize_t) sizeof(region.width))
464 count=read(file,®ion.height,sizeof(region.height));
465 if (count != (ssize_t) sizeof(region.height))
467 count=read(file,®ion.x,sizeof(region.x));
468 if (count != (ssize_t) sizeof(region.width))
470 count=read(file,®ion.y,sizeof(region.y));
471 if (count != (ssize_t) sizeof(region.y))
473 length=(size_t) (region.width*cache_info->number_channels*sizeof(Quantum));
474 exception=AcquireExceptionInfo();
475 p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
477 exception=DestroyExceptionInfo(exception);
478 if (p != (const Quantum *) NULL)
480 count=write(file,p,length);
481 if (count != (ssize_t) length)
486 static MagickBooleanType WriteDistributeCache(int file,
487 const MagickSizeType session_key)
516 exception=AcquireExceptionInfo();
517 (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
518 image=(Image *) GetImageRegistry(ImageRegistryType,key,exception);
519 exception=DestroyExceptionInfo(exception);
520 if (image == (Image *) NULL)
522 cache_info=(CacheInfo *) image->cache;
523 count=read(file,®ion.width,sizeof(region.width));
524 if (count != (ssize_t) sizeof(region.width))
526 count=read(file,®ion.height,sizeof(region.height));
527 if (count != (ssize_t) sizeof(region.height))
529 count=read(file,®ion.x,sizeof(region.x));
530 if (count != (ssize_t) sizeof(region.width))
532 count=read(file,®ion.y,sizeof(region.y));
533 if (count != (ssize_t) sizeof(region.y))
535 length=(size_t) (region.width*cache_info->number_channels*sizeof(Quantum));
536 p=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
538 exception=DestroyExceptionInfo(exception);
539 if (p != (Quantum *) NULL)
541 count=read(file,p,length);
542 if (count != (ssize_t) length)
544 status=SyncAuthenticPixels(image,exception);
548 static void *DistributePixelCacheClient(void *socket)
572 session[MaxTextExtent];
575 Generate session key.
577 shared_secret=GetPolicyValue("shared-secret");
578 if (shared_secret == (const char *) NULL)
579 ThrowFatalException(CacheFatalError,"shared secret expected");
580 (void) CopyMagickString((char *) session,shared_secret,MaxTextExtent-
581 DPCSessionKeyLength);
582 random_info=AcquireRandomInfo();
583 secret=GetRandomKey(random_info,DPCSessionKeyLength);
584 (void) memcpy(session+strlen(shared_secret),GetStringInfoDatum(secret),
585 DPCSessionKeyLength);
586 session_key=CRC64(session,strlen(shared_secret)+DPCSessionKeyLength);
587 random_info=DestroyRandomInfo(random_info);
588 client_socket=(*(int *) socket);
589 count=write(client_socket,GetStringInfoDatum(secret),DPCSessionKeyLength);
590 secret=DestroyStringInfo(secret);
593 count=read(client_socket,&command,1);
596 count=read(client_socket,&key,sizeof(key));
597 if ((count != (ssize_t) sizeof(key)) && (key != session_key))
604 status=CreateDistributeCache(client_socket,session_key);
609 status=DestroyDistributeCache(client_socket,session_key);
614 status=ReadDistributeCache(client_socket,session_key);
619 status=WriteDistributeCache(client_socket,session_key);
625 count=write(client_socket,&status,sizeof(status));
626 if (count != (ssize_t) sizeof(status))
629 return((void *) NULL);
632 MagickExport void DistributePixelCacheServer(const size_t port,
633 ExceptionInfo *exception)
635 #if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
650 Launch distributed pixel cache server.
652 server_socket=socket(AF_INET,SOCK_STREAM,0);
653 address.sin_family=AF_INET;
654 address.sin_port=htons(port);
655 address.sin_addr.s_addr=htonl(INADDR_ANY);
656 status=bind(server_socket,(struct sockaddr *) &address,(socklen_t)
659 ThrowFatalException(CacheFatalError,"UnableToBind");
660 status=listen(server_socket,32);
662 ThrowFatalException(CacheFatalError,"UnableToListen");
663 pthread_attr_init(&attributes);
672 length=(socklen_t) sizeof(address);
673 client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
674 if (client_socket == -1)
675 ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
676 status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
677 (void *) &client_socket);
679 ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
681 (void) close(server_socket);
683 ThrowFatalException(MissingDelegateError,"distributed pixel cache");
688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692 % O p e n D i s t r i b u t e P i x e l C a c h e %
696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698 % OpenDistributePixelCache() opens a pixel cache on a remote server.
700 % The format of the OpenDistributePixelCache method is:
702 % MagickBooleanType *OpenDistributePixelCache(
703 % DistributeCacheInfo *distribute_cache_info,Image *image)
705 % A description of each parameter follows:
707 % o distribute_cache_info: the distributed cache info.
709 % o image: the image.
712 MagickPrivate MagickBooleanType OpenDistributePixelCache(
713 DistributeCacheInfo *distribute_cache_info,Image *image)
727 assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
728 assert(distribute_cache_info->signature == MagickSignature);
729 assert(image != (Image *) NULL);
730 assert(image->signature == MagickSignature);
731 file=distribute_cache_info->file;
732 session_key=distribute_cache_info->session_key;
733 count=write(file,"c",1);
736 count=write(file,&session_key,sizeof(session_key));
737 if (count != (ssize_t) sizeof(session_key))
739 count=write(file,&image->columns,sizeof(image->columns));
740 if (count != (ssize_t) sizeof(image->columns))
742 count=write(file,&image->rows,sizeof(image->rows));
743 if (count != (ssize_t) sizeof(image->rows))
745 count=write(file,&image->number_channels,sizeof(image->number_channels));
746 if (count != (ssize_t) sizeof(image->number_channels))
748 count=read(file,&status,sizeof(status));
749 if (count != (ssize_t) sizeof(status))
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759 % R e a d D i s t r i b u t e P i x e l C a c h e %
763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765 % ReadDistributePixelCache() reads pixels from the specified region of the
766 % distributed pixel cache.
768 % The format of the ReadDistributePixelCache method is:
770 % MagickBooleanType *ReadDistributePixelCache(
771 % DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
774 % A description of each parameter follows:
776 % o distribute_cache_info: the distributed cache info.
778 % o image: the image.
780 % o region: read the pixels from this region of the image.
782 % o pixels: read these pixels from the pixel cache.
785 MagickPrivate MagickBooleanType ReadDistributePixelCache(
786 DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
816 assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
817 assert(distribute_cache_info->signature == MagickSignature);
818 assert(region != (RectangleInfo *) NULL);
819 assert(pixels != (Quantum *) NULL);
820 exception=AcquireExceptionInfo();
821 (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
822 image=(Image *) GetImageRegistry(ImageRegistryType,key,exception);
823 exception=DestroyExceptionInfo(exception);
824 if (image == (Image *) NULL)
826 cache_info=(CacheInfo *) image->cache;
827 file=distribute_cache_info->file;
828 session_key=distribute_cache_info->session_key;
829 count=write(file,"r",1);
832 count=write(file,&session_key,sizeof(session_key));
833 if (count != (ssize_t) sizeof(session_key))
835 count=write(file,®ion->width,sizeof(region->width));
836 if (count != (ssize_t) sizeof(region->width))
838 count=write(file,®ion->height,sizeof(region->height));
839 if (count != (ssize_t) sizeof(region->height))
841 count=write(file,®ion->x,sizeof(region->x));
842 if (count != (ssize_t) sizeof(region->x))
844 count=write(file,®ion->y,sizeof(region->x));
845 if (count != (ssize_t) sizeof(region->y))
847 length=(size_t) (region->width*cache_info->number_channels*sizeof(Quantum));
848 count=read(file,(unsigned char *) pixels,length);
849 if (count != (ssize_t) length)
851 count=read(file,&status,sizeof(status));
852 if (count != (ssize_t) sizeof(status))
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
862 % R e l i n q u i s h D i s t r i b u t e P i x e l C a c h e %
866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868 % RelinquishDistributePixelCache() frees resources acquired with
869 % OpenDistributePixelCache().
871 % The format of the RelinquishDistributePixelCache method is:
873 % void RelinquishDistributePixelCache(
874 % DistributeCacheInfo *distribute_cache_info)
876 % A description of each parameter follows:
878 % o distribute_cache_info: the distributed cache info.
881 MagickPrivate void RelinquishDistributePixelCache(
882 DistributeCacheInfo *distribute_cache_info)
890 assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
891 assert(distribute_cache_info->signature == MagickSignature);
892 file=distribute_cache_info->file;
893 session_key=distribute_cache_info->session_key;
894 (void) write(file,"c",1);
895 (void) write(file,&session_key,sizeof(session_key));
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903 % W r i t e D i s t r i b u t e P i x e l C a c h e %
907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
909 % WriteDistributePixelCache() writes image pixels to the specified region of
910 % the distributed pixel cache.
913 % The format of the WriteDistributePixelCache method is:
915 % MagickBooleanType *WriteDistributePixelCache(
916 % DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
917 % const Quantum *pixels)
919 % A description of each parameter follows:
921 % o distribute_cache_info: the distributed cache info.
923 % o image: the image.
925 % o region: write the pixels to this region of the image.
927 % o pixels: write these pixels to the pixel cache.
930 MagickPrivate MagickBooleanType WriteDistributePixelCache(
931 DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
932 const Quantum *pixels)
961 assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
962 assert(distribute_cache_info->signature == MagickSignature);
963 assert(region != (RectangleInfo *) NULL);
964 assert(pixels != (Quantum *) NULL);
965 exception=AcquireExceptionInfo();
966 (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
967 image=(Image *) GetImageRegistry(ImageRegistryType,key,exception);
968 exception=DestroyExceptionInfo(exception);
969 if (image == (Image *) NULL)
971 cache_info=(CacheInfo *) image->cache;
972 file=distribute_cache_info->file;
973 session_key=distribute_cache_info->session_key;
974 count=write(file,"u",1);
977 count=write(file,&session_key,sizeof(session_key));
978 if (count != (ssize_t) sizeof(session_key))
980 count=write(file,®ion->width,sizeof(region->width));
981 if (count != (ssize_t) sizeof(region->width))
983 count=write(file,®ion->height,sizeof(region->height));
984 if (count != (ssize_t) sizeof(region->height))
986 count=write(file,®ion->x,sizeof(region->x));
987 if (count != (ssize_t) sizeof(region->x))
989 count=write(file,®ion->y,sizeof(region->x));
990 if (count != (ssize_t) sizeof(region->y))
992 length=(size_t) (region->width*cache_info->number_channels*sizeof(Quantum));
993 count=write(file,(unsigned char *) pixels,length);
994 if (count != (ssize_t) length)
996 count=read(file,&status,sizeof(status));
997 if (count != (ssize_t) sizeof(status))