Coverage Report

Created: 2026-01-20 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/resource.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
%
5
% This program is covered by multiple licenses, which are described in
6
% Copyright.txt. You should have received a copy of Copyright.txt with this
7
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8
%
9
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10
%                                                                             %
11
%                                                                             %
12
%                                                                             %
13
%           RRRR    EEEEE   SSSSS   OOO   U   U  RRRR    CCCC  EEEEE          %
14
%           R   R   E       SS     O   O  U   U  R   R  C      E              %
15
%           RRRR    EEE      SSS   O   O  U   U  RRRR   C      EEE            %
16
%           R R     E          SS  O   O  U   U  R R    C      E              %
17
%           R  R    EEEEE   SSSSS   OOO    UUU   R  R    CCCC  EEEEE          %
18
%                                                                             %
19
%                                                                             %
20
%                        Get/Set ImageMagick Resources.                       %
21
%                                                                             %
22
%                                                                             %
23
%                              Software Design                                %
24
%                                John Cristy                                  %
25
%                               September 2002                                %
26
%                                                                             %
27
%                            Completely Re-written                            %
28
%                               Bob Friesenhahn                               %
29
%                                 April 2008                                  %
30
%                                                                             %
31
%                                                                             %
32
%                                                                             %
33
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34
%
35
%
36
*/
37

38
/*
39
  Include declarations.
40
*/
41
#include "magick/studio.h"
42
#include "magick/log.h"
43
#include "magick/resource.h"
44
#include "magick/semaphore.h"
45
#include "magick/utility.h"
46

47
/*
48
  Define  declarations.
49
*/
50
/* #define MagickResourceInfinity ((magick_int64_t) (~((magick_uint64_t) 0) >> 1)) */
51
892M
#define ResourceInfoMaxIndex ((unsigned int) (sizeof(resource_info)/sizeof(resource_info[0])-1))
52
53
/*
54
  Typedef declarations.
55
*/
56
typedef enum
57
{
58
  AbsoluteLimit,
59
  SummationLimit
60
} LimitType;
61
62
/*
63
  Definition of a resource.  We intentionally use signed types to
64
  store 'value' and 'limit'.
65
*/
66
typedef struct _ResourceInfo
67
{
68
  const char
69
    name[8],
70
    units[2],
71
    env[20];
72
73
  magick_intmax_t
74
    value;
75
76
  magick_intmax_t
77
    minimum,
78
    maximum,
79
    highwater;
80
81
  LimitType
82
    limit_type;
83
84
  SemaphoreInfo
85
    *semaphore;
86
87
} ResourceInfo;
88

89
/*
90
  Global declarations.
91
*/
92
93
/*
94
  Array must be in same order as ResourceType enum
95
*/
96
#define PIXEL_LIMIT (INT_MAX/(sizeof(PixelPacket)))
97
static ResourceInfo resource_info[] =
98
  {
99
   { "",       "",  "",                    0, 0,    MagickResourceInfinity, 0, AbsoluteLimit, 0  },
100
   { "disk",   "B", "MAGICK_LIMIT_DISK",   0, 0,    MagickResourceInfinity, 0, SummationLimit, 0 },
101
   { "files",  "",  "MAGICK_LIMIT_FILES",  0, 32,   256,                    0, SummationLimit, 0 },
102
   { "map",    "B", "MAGICK_LIMIT_MAP",    0, 0,    MagickResourceInfinity, 0, SummationLimit, 0 },
103
   { "memory", "B", "MAGICK_LIMIT_MEMORY", 0, 0,    MagickResourceInfinity, 0, SummationLimit, 0 },
104
   { "pixels", "P", "MAGICK_LIMIT_PIXELS", 0, 1,    MagickResourceInfinity, 0, AbsoluteLimit, 0  },
105
   { "threads", "", "OMP_NUM_THREADS",     1, 1,    MagickResourceInfinity, 0, AbsoluteLimit, 0  },
106
   { "width",  "P", "MAGICK_LIMIT_WIDTH",  0, 1,    PIXEL_LIMIT,            0, AbsoluteLimit, 0  },
107
   { "height", "P", "MAGICK_LIMIT_HEIGHT", 0, 1,    PIXEL_LIMIT,            0, AbsoluteLimit, 0  },
108
   { "read",   "B", "MAGICK_LIMIT_READ",   0, 4096, MagickResourceInfinity, 0, AbsoluteLimit, 0  },
109
   { "write",  "B", "MAGICK_LIMIT_WRITE",  0, 4096, MagickResourceInfinity, 0, AbsoluteLimit, 0  },
110
   { "images", "",  "MAGICK_LIMIT_IMAGES",  0, 0,   MagickResourceInfinity, 0, SummationLimit, 0 }
111
  };
112

113
/*
114
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115
%                                                                             %
116
%                                                                             %
117
%                                                                             %
118
%   A c q u i r e M a g i c k R e s o u r c e                                 %
119
%                                                                             %
120
%                                                                             %
121
%                                                                             %
122
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123
%
124
%  AcquireMagickResource() acquires resources of the specified type.
125
%  MagickTrue is returned if the specified resource is available otherwise
126
%  MagickFalse.
127
%
128
%  The format of the AcquireMagickResource() method is:
129
%
130
%      MagickPassFail AcquireMagickResource(const ResourceType type,
131
%        const magick_int64_t size)
132
%
133
%  A description of each parameter follows:
134
%
135
%    o type: The type of resource.
136
%
137
%    o size: The number of bytes needed from for this resource.
138
%
139
%
140
*/
141
static ResourceInfo *GetResourceInfo(const ResourceType type)
142
892M
{
143
892M
  ResourceInfo
144
892M
    *info;
145
146
892M
  unsigned int
147
892M
    index;
148
149
892M
  info = (ResourceInfo *) NULL;
150
892M
  index = type;
151
892M
  if ((index != 0) && (index <= ResourceInfoMaxIndex))
152
892M
    info = &resource_info[index];
153
154
892M
  return info;
155
892M
}
156
157
MagickExport MagickPassFail
158
AcquireMagickResource(const ResourceType type,
159
                      const magick_uint64_t size)
160
389M
{
161
389M
  ResourceInfo
162
389M
    *info;
163
164
389M
  MagickPassFail
165
389M
    status;
166
167
389M
  status=MagickPass;
168
169
389M
  if ((info=GetResourceInfo(type)))
170
389M
    {
171
389M
      magick_uint64_t
172
389M
        value=0;
173
174
389M
      switch(info->limit_type)
175
389M
        {
176
10.7M
        case AbsoluteLimit:
177
10.7M
          {
178
            /*
179
              Limit depends only on the currently requested size.
180
            */
181
10.7M
            value=info->value;
182
10.7M
            if ((info->maximum != MagickResourceInfinity) &&
183
10.7M
                (size > (magick_uint64_t) info->maximum))
184
1.06M
              {
185
1.06M
                status=MagickFail;
186
1.06M
              }
187
9.68M
            else
188
9.68M
              {
189
9.68M
                LockSemaphoreInfo(info->semaphore);
190
9.68M
                if (size > (magick_uint64_t) info->highwater)
191
2.51k
                  info->highwater = size;
192
9.68M
                UnlockSemaphoreInfo(info->semaphore);
193
9.68M
              }
194
10.7M
            break;
195
0
          }
196
378M
        case SummationLimit:
197
378M
          {
198
            /*
199
              Limit depends on sum of previous allocations as well as
200
              the currently requested size.
201
            */
202
378M
            LockSemaphoreInfo(info->semaphore);
203
378M
            value=info->value+size;
204
378M
            if ((info->maximum != MagickResourceInfinity) &&
205
377M
                (value > (magick_uint64_t) info->maximum))
206
963
              {
207
963
                value=info->value;
208
963
                status=MagickFail;
209
963
              }
210
378M
            else
211
378M
              {
212
378M
                info->value=value;
213
378M
                if (value > (magick_uint64_t) info->highwater)
214
5.47M
                  info->highwater = value;
215
378M
              }
216
378M
            UnlockSemaphoreInfo(info->semaphore);
217
378M
            break;
218
0
          }
219
389M
        }
220
389M
      if (IsEventLogged(ResourceEvent))
221
0
        {
222
0
          char
223
0
            f_limit[MaxTextExtent],
224
0
            f_size[MaxTextExtent],
225
0
            f_value[MaxTextExtent];
226
227
0
          if (info->maximum == MagickResourceInfinity)
228
0
            {
229
0
              strlcpy(f_limit,"Unlimited",sizeof(f_limit));
230
0
            }
231
0
          else
232
0
            {
233
0
              FormatSize(info->maximum, f_limit);
234
0
              strlcat(f_limit,info->units,sizeof(f_limit));
235
0
            }
236
237
0
          FormatSize(size, f_size);
238
0
          strlcat(f_size,info->units,sizeof(f_size));
239
240
0
          if (info->limit_type == AbsoluteLimit)
241
0
            {
242
0
              strlcpy(f_value,"----",sizeof(f_value));
243
0
            }
244
0
          else
245
0
            {
246
0
              FormatSize(value, f_value);
247
0
              strlcat(f_value,info->units,sizeof(f_value));
248
0
            }
249
250
0
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
251
0
                                "%s %s%s/%s/%s",
252
0
                                info->name,
253
0
                                (status ? "+" : "!"),
254
0
                                f_size,
255
0
                                f_value,
256
0
                                f_limit);
257
0
        }
258
389M
    }
259
260
389M
  return(status);
261
389M
}
262

263
/*
264
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265
%                                                                             %
266
%                                                                             %
267
%                                                                             %
268
+   D e s t r o y M a g i c k R e s o u r c e s                               %
269
%                                                                             %
270
%                                                                             %
271
%                                                                             %
272
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
273
%
274
%  DestroyMagickResources() destroys the resource environment.
275
%
276
%  The format of the DestroyMagickResources() method is:
277
%
278
%      DestroyMagickResources(void)
279
%
280
%
281
*/
282
MagickExport void DestroyMagickResources(void)
283
0
{
284
0
  size_t
285
0
    index;
286
287
0
  if (IsEventLogged(ResourceEvent))
288
0
    {
289
#if 0
290
      const ResourceInfo *disc_info = GetResourceInfo(DiskResource);
291
      const ResourceInfo *file_info = GetResourceInfo(FileResource);
292
      const ResourceInfo *map_info = GetResourceInfo(MapResource);
293
      const ResourceInfo *memory_info = GetResourceInfo(MemoryResource);
294
      const ResourceInfo *images_info = GetResourceInfo(ImagesResource);
295
296
      (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
297
                            "Resource Consumption Summary\n"
298
                            "    %c%s Limit: %" MAGICK_INTMAX_F "d, Maximum Used: %" MAGICK_INTMAX_F "d\n"
299
                            "    %c%s Limit: %" MAGICK_INTMAX_F "d, Maximum Used: %" MAGICK_INTMAX_F "d\n"
300
                            "    %c%s Limit: %" MAGICK_INTMAX_F "d, Maximum Used: %" MAGICK_INTMAX_F "d\n"
301
                            "    %c%s Limit: %" MAGICK_INTMAX_F "d, Maximum Used: %" MAGICK_INTMAX_F "d\n"
302
                            "    %c%s Limit: %" MAGICK_INTMAX_F "d, Maximum Used: %" MAGICK_INTMAX_F "d\n",
303
                            toupper((int) disc_info->name[0]),disc_info->name+1, disc_info->maximum, (magick_int64_t) disc_info->highwater,
304
                            toupper((int) file_info->name[0]),file_info->name+1, file_info->maximum, (magick_int64_t) file_info->highwater,
305
                            toupper((int) map_info->name[0]),map_info->name+1, map_info->maximum, (magick_int64_t) map_info->highwater,
306
                            toupper((int) memory_info->name[0]),memory_info->name+1, memory_info->maximum, (magick_int64_t) memory_info->highwater,
307
                            toupper((int) images_info->name[0]),images_info->name+1, images_info->maximum, (magick_int64_t) images_info->highwater
308
                            );
309
#endif
310
0
#if 1
311
0
      const ResourceInfo *info;
312
0
      for (index = 1; index < ArraySize(resource_info); index++)
313
0
        {
314
0
          info=&resource_info[index];
315
0
          if (info->limit_type != SummationLimit && info->highwater == 0)
316
0
            continue;
317
0
          LockSemaphoreInfo(info->semaphore);
318
0
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
319
0
                                "Resource - %c%s Limit: %" MAGICK_INTMAX_F "d,"
320
0
                                " Maximum Used: %" MAGICK_INTMAX_F "d",
321
0
                                toupper((int) info->name[0]),info->name+1,
322
0
                                info->maximum, (magick_int64_t) info->highwater);
323
0
          UnlockSemaphoreInfo(info->semaphore);
324
0
        }
325
0
#endif
326
0
    }
327
328
#if defined(DEBUG_MAGICK_RESOURCES) && DEBUG_MAGICK_RESOURCES
329
  {
330
    const ResourceInfo *info;
331
332
    for (index = 1; index < ArraySize(resource_info); index++)
333
      {
334
        info=&resource_info[index];
335
        if (info->limit_type != SummationLimit)
336
          continue;
337
        LockSemaphoreInfo(info->semaphore);
338
        if (info->value != 0)
339
          {
340
            fprintf(stderr,"Resource[%s] %s! %" MAGICK_INTMAX_F "d remaining\n",
341
                    info->name, info->value < 0 ? "underflow" : "leak",
342
                    (magick_int64_t) info->value);
343
#if defined(DEBUG_MAGICK_RESOURCES_ASSERT) && DEBUG_MAGICK_RESOURCES_ASSERT
344
            assert(info->value == 0);
345
#endif /* if defined(DEBUG_MAGICK_RESOURCES_ASSERT) && DEBUG_MAGICK_RESOURCES_ASSERT */
346
          }
347
        UnlockSemaphoreInfo(info->semaphore);
348
      }
349
  }
350
#endif /* if defined(DEBUG_MAGICK_RESOURCES) && DEBUG_MAGICK_RESOURCES */
351
352
0
  for (index = 1; index < ArraySize(resource_info); index++)
353
0
    DestroySemaphoreInfo(&resource_info[index].semaphore);
354
0
}
355

356
/*
357
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358
%                                                                             %
359
%                                                                             %
360
%                                                                             %
361
%   G e t M a g i c k R e s o u r c e                                         %
362
%                                                                             %
363
%                                                                             %
364
%                                                                             %
365
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366
%
367
%  GetMagickResource() returns the current consumption level for the
368
%  specified resource type.
369
%
370
%  The format of the GetMagickResource() method is:
371
%
372
%      magick_uint64_t GetMagickResource(const ResourceType type)
373
%
374
%  A description of each parameter follows:
375
%
376
%    o type: The type of resource.
377
%
378
%
379
*/
380
MagickExport magick_int64_t GetMagickResource(const ResourceType type)
381
1.49M
{
382
1.49M
  ResourceInfo
383
1.49M
    *info;
384
385
1.49M
  magick_int64_t
386
1.49M
    resource;
387
388
1.49M
  resource=0;
389
390
1.49M
  if ((info=GetResourceInfo(type)))
391
1.49M
    {
392
1.49M
      LockSemaphoreInfo(info->semaphore);
393
1.49M
      resource=info->value;
394
1.49M
      UnlockSemaphoreInfo(info->semaphore);
395
1.49M
    }
396
397
1.49M
  return(resource);
398
1.49M
}
399

400
/*
401
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
402
%                                                                             %
403
%                                                                             %
404
%                                                                             %
405
%   G e t M a g i c k R e s o u r c e L i m i t                               %
406
%                                                                             %
407
%                                                                             %
408
%                                                                             %
409
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410
%
411
%  GetMagickResourceLimit() returns the current maximum limit for the
412
%  specified resource type.  Returns a maximum 64-bit integer value if
413
%  the resource has not been limited.
414
%
415
%  The format of the GetMagickResourceLimit() method is:
416
%
417
%      magick_int64_t GetMagickResourceLimit(const ResourceType type)
418
%
419
%  A description of each parameter follows:
420
%
421
%    o type: The type of resource.
422
%
423
%
424
*/
425
MagickExport magick_int64_t GetMagickResourceLimit(const ResourceType type)
426
117M
{
427
117M
  ResourceInfo
428
117M
    *info;
429
430
117M
  magick_int64_t
431
117M
    resource;
432
433
117M
  resource=0;
434
435
117M
  if ((info=GetResourceInfo(type)))
436
117M
    {
437
117M
      LockSemaphoreInfo(info->semaphore);
438
117M
      resource=info->maximum;
439
117M
      UnlockSemaphoreInfo(info->semaphore);
440
117M
    }
441
442
117M
  return(resource);
443
117M
}
444

445
/*
446
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447
%                                                                             %
448
%                                                                             %
449
%                                                                             %
450
+   I n i t i a l i z e M a g i c k R e s o u r c e s                         %
451
%                                                                             %
452
%                                                                             %
453
%                                                                             %
454
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
455
%
456
%  InitializeMagickResources() initializes the resource environment.
457
%
458
%  The format of the InitializeMagickResources() method is:
459
%
460
%      InitializeMagickResources(void)
461
%
462
%
463
*/
464
MagickExport void InitializeMagickResources(void)
465
252
{
466
252
  magick_int64_t
467
252
    max_disk=-1,
468
252
    max_files=256,
469
252
    max_map=4096,
470
252
    max_memory=1024,
471
252
    max_pixels=-1,
472
252
    max_threads=1,
473
252
    max_width=-1,
474
252
    max_height=-1,
475
252
    max_read=-1,
476
252
    max_write=-1,
477
252
    max_images=-1;
478
479
252
  size_t
480
252
    index;
481
482
  /*
483
    Allocate semaphores.
484
  */
485
3.02k
  for (index = 1; index < ArraySize(resource_info); index++)
486
2.77k
    resource_info[index].semaphore=AllocateSemaphoreInfo();
487
488
  /*
489
    Set Magick resource limits.
490
  */
491
252
#if defined(POSIX)
492
252
  {
493
252
    unsigned long
494
252
      total_memory=0;
495
496
252
#  if  defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES)
497
252
    {
498
252
      long
499
252
        pagesize=-1,
500
252
        pages=-1;
501
      /*
502
        Compute total physical memory based on number of memory pages,
503
        and page size.
504
      */
505
252
      pages=sysconf(_SC_PHYS_PAGES);
506
252
      pagesize = MagickGetMMUPageSize();
507
508
252
      if (pages > 0 && pagesize > 0)
509
252
        total_memory=((pages+512)/1024)*((pagesize+512)/1024);
510
252
      (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
511
252
                            "Total physical memory %ld MB (%ld pages and %ld bytes per page)",
512
252
                            total_memory, pages, pagesize);
513
252
    }
514
#  elif defined(MAGICK_PHYSICAL_MEMORY_COMMAND) && defined(HAVE_POPEN)
515
    {
516
      double
517
        bytes=0;
518
519
      FILE
520
        *command;
521
      /*
522
        Execute the external command defined by
523
        MAGICK_PHYSICAL_MEMORY_COMMAND to obtain the total physical
524
        memory in bytes.  This external command should be quite speedy
525
        or else it will impact the time to start GraphicsMagick.
526
      */
527
      if ((command = popen(MAGICK_PHYSICAL_MEMORY_COMMAND, "r")) != NULL)
528
        {
529
          if (fscanf(command,"%lf",&bytes) == 1)
530
            total_memory=(bytes/(1024*1024));
531
          (void) pclose(command);
532
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
533
                                "Total physical memory %ld MB",total_memory);
534
        }
535
    }
536
#  endif
537
538
252
    if (total_memory > 0)
539
252
      {
540
        /*
541
          We are not able to use more than size_t worth of address
542
          space.  On 32 bit systems, we can usually only use half or
543
          three quarters of it.  Regardless, we need address space
544
          for more than just image data.
545
        */
546
252
        size_t
547
252
          size_t_max = (size_t) ~0UL;
548
549
252
        size_t_max /= (1024U*1024U);
550
252
        max_memory = Min(size_t_max/2-100,total_memory);
551
252
        max_map = Min(size_t_max/2-100,2*total_memory);
552
252
      }
553
252
  }
554
252
#endif /* defined(POSIX) */
555
556
#if defined(MSWINDOWS)
557
  {
558
    long
559
      total_physical_memory=0,
560
      total_virtual_memory=0;
561
562
    /*
563
      GlobalMemoryStatusEx is necessary to handle results for
564
      large-memory (>4GB) machines (and to provide accurate results
565
      for 2 to 4 GB of memory), but it is not available on older
566
      versions of Windows.  Windows 32-bit applications are still
567
      usually limited to addressing only 2 GB of memory even if the
568
      system provides more (except for certain server versions of
569
      Windows with applications linked with the /LARGEADDRESSAWARE
570
      option). Test for API existence prior to using it.  MinGW
571
      headers currently lack support for this API so compilation of
572
      supportive code is made optional.
573
    */
574
#if defined(HAVE_GLOBALMEMORYSTATUSEX)
575
    if (NTKernelAPISupported("GlobalMemoryStatusEx"))
576
      {
577
        MEMORYSTATUSEX
578
          stat_ex;
579
580
        (void) memset(&stat_ex,0,sizeof(stat_ex));
581
        stat_ex.dwLength=sizeof(stat_ex);
582
        if (GlobalMemoryStatusEx(&stat_ex))
583
          {
584
            total_physical_memory=(long)(stat_ex.ullTotalPhys/1048576UL);
585
            total_virtual_memory=(long)(stat_ex.ullTotalVirtual/1048576UL);
586
          }
587
        else
588
          {
589
            (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
590
                                  "GlobalMemoryStatusEx() call failed! (error %ld)",GetLastError());
591
          }
592
      }
593
#endif
594
595
    if (total_physical_memory == 0)
596
      {
597
        MEMORYSTATUS
598
          stat;
599
600
        GlobalMemoryStatus(&stat);
601
        total_physical_memory=stat.dwTotalPhys/1048576;
602
        total_virtual_memory=stat.dwTotalVirtual/1048576;
603
      }
604
    (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
605
                          "Total physical memory %ld MB, Total virtual memory %ld MB",
606
                          total_physical_memory, total_virtual_memory);
607
608
    max_memory=Min(total_physical_memory,total_virtual_memory);
609
    /*
610
      We will default to using at most 80% of available memory for image data.
611
    */
612
    max_memory=(long)(0.8*max_memory);
613
    /*
614
      Use the maximum memory as our default memory map limit.  Memory
615
      mapping files larger than available RAM can cause VM thrashing.
616
      This implies that memory mapping won't get used much.  However,
617
      it is possible for heap memory allocations to fail (due to heap
618
      fragmentation) whereas memory mapping still succeeds.
619
    */
620
    max_map=max_memory;
621
622
    /*
623
      Windows lowio level supports up to 2048 open files.
624
      Reserve 512 handles for other uses.
625
    */
626
    max_files=2048-512;
627
  }
628
#endif /* defined(MSWINDOWS) */
629
630
  /*
631
    Compute fundamental limits based on build parameters.  Image size
632
    is fundamentally limited by how many pixels may reside in one row.
633
  */
634
252
  if (sizeof(size_t) == 4)
635
0
    max_pixels=(magick_int64_t) ((unsigned long) ULONG_MAX/(5*sizeof(Quantum)));
636
637
638
  /*
639
    Disk, map, and memory are in units of MB and need to be scaled up.
640
  */
641
  /* if (max_disk > 0) */
642
  /*   max_disk  *= 1024UL*1024UL; */
643
252
  if (max_map > 0)
644
252
    max_map   *= 1024UL*1024UL;
645
252
  if (max_memory > 0)
646
252
    max_memory *= 1024UL*1024UL;
647
648
  /*
649
    Support using environment variables to set limits
650
  */
651
252
  {
652
252
    const char
653
252
      *envp;
654
655
252
    if ((envp=getenv("MAGICK_LIMIT_DISK")))
656
0
      max_disk=MagickSizeStrToInt64(envp,1024);
657
658
252
    if ((envp=getenv("MAGICK_LIMIT_FILES")))
659
0
      max_files=MagickSizeStrToInt64(envp,1024);
660
661
252
    if ((envp=getenv("MAGICK_LIMIT_MAP")))
662
0
      max_map=MagickSizeStrToInt64(envp,1024);
663
664
252
    if ((envp=getenv("MAGICK_LIMIT_MEMORY")))
665
0
      max_memory=MagickSizeStrToInt64(envp,1024);
666
667
252
    if ((envp=getenv("MAGICK_LIMIT_PIXELS")))
668
0
      max_pixels=MagickSizeStrToInt64(envp,1024);
669
670
252
    if ((envp=getenv("MAGICK_LIMIT_WIDTH")))
671
0
      max_width=MagickSizeStrToInt64(envp,1024);
672
673
252
    if ((envp=getenv("MAGICK_LIMIT_HEIGHT")))
674
0
      max_height=MagickSizeStrToInt64(envp,1024);
675
676
252
    if ((envp=getenv("MAGICK_LIMIT_READ")))
677
0
      max_read=MagickSizeStrToInt64(envp,1024);
678
679
252
    if ((envp=getenv("MAGICK_LIMIT_WRITE")))
680
0
      max_write=MagickSizeStrToInt64(envp,1024);
681
682
252
    if ((envp=getenv("MAGICK_LIMIT_IMAGES")))
683
0
      max_images=MagickSizeStrToInt64(envp,1024);
684
685
#if defined(HAVE_OPENMP)
686
    max_threads=omp_get_num_procs();
687
    (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
688
                          "%i CPU cores are available",(int) max_threads);
689
    if ((envp=getenv("OMP_NUM_THREADS")))
690
      {
691
        max_threads=MagickSizeStrToInt64(envp,1024);
692
        (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
693
                              "OMP_NUM_THREADS requests %i threads",(int) max_threads);
694
      }
695
    if (max_threads < 1)
696
      max_threads=1;
697
    omp_set_num_threads((int) max_threads);
698
    max_threads=omp_get_max_threads();
699
#endif /* HAVE_OPENMP */
700
252
  }
701
702
252
#if defined(POSIX)
703
252
#  if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
704
252
    {
705
252
      struct rlimit
706
252
        rlimits;
707
708
252
      rlim_t
709
252
        margin,
710
252
        target;
711
712
252
      margin=128;
713
252
      target=(rlim_t) max_files+margin;
714
252
      if (getrlimit(RLIMIT_NOFILE, &rlimits) != -1)
715
252
        {
716
252
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
717
252
                                "System file open limits are %lu soft,"
718
252
                                " %lu hard",
719
252
                                (unsigned long) rlimits.rlim_cur,
720
252
                                (unsigned long) rlimits.rlim_max);
721
252
#    if defined(HAVE_SETRLIMIT)
722
252
          if (rlimits.rlim_max < target)
723
0
            rlimits.rlim_cur=rlimits.rlim_max;
724
252
          if (rlimits.rlim_cur < target)
725
0
            {
726
0
              (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
727
0
                                    "Increasing file open soft limit from %lu "
728
0
                                    "to %lu",
729
0
                                    (unsigned long) rlimits.rlim_cur,
730
0
                                    (unsigned long) target);
731
0
              rlimits.rlim_cur=target;
732
0
              (void) setrlimit(RLIMIT_NOFILE, &rlimits);
733
0
            }
734
252
          if (getrlimit(RLIMIT_NOFILE, &rlimits) != -1)
735
252
            {
736
252
              if (rlimits.rlim_cur < target)
737
0
                {
738
0
                  if (rlimits.rlim_cur > margin*2)
739
0
                    max_files=rlimits.rlim_cur-margin;
740
0
                  else
741
0
                    max_files=rlimits.rlim_cur/2;
742
0
                }
743
252
            }
744
252
#    endif
745
252
        }
746
252
    }
747
#  elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
748
    {
749
      long
750
        margin,
751
        open_max,
752
        target;
753
754
      margin=128;
755
      target=max_files+margin;
756
      open_max=sysconf(_SC_OPEN_MAX);
757
      (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
758
                            "System file open limit is %lu",
759
                            (unsigned long) open_max);
760
      if (open_max < target)
761
        {
762
          if (open_max > margin*2)
763
            max_files=open_max-margin;
764
          else
765
            max_files=open_max/2;
766
        }
767
    }
768
#  endif
769
770
252
#endif
771
772
252
  if (max_disk >= 0)
773
0
    (void) SetMagickResourceLimit(DiskResource,max_disk);
774
252
  if (max_files >= 0)
775
252
    (void) SetMagickResourceLimit(FileResource,max_files);
776
252
  if (max_map >= 0)
777
252
    (void) SetMagickResourceLimit(MapResource,max_map);
778
252
  if (max_memory >= 0)
779
252
    (void) SetMagickResourceLimit(MemoryResource,max_memory);
780
252
  if (max_pixels >= 0)
781
0
    (void) SetMagickResourceLimit(PixelsResource,max_pixels);
782
252
  if (max_threads >= 0)
783
252
    {
784
252
      (void) SetMagickResourceLimit(ThreadsResource,max_threads);
785
252
      _UpdateMagickResourceHighwater(ThreadsResource,max_threads);
786
252
    }
787
252
  if (max_width >= 0)
788
0
    (void) SetMagickResourceLimit(WidthResource,max_width);
789
252
  if (max_height >= 0)
790
0
    (void) SetMagickResourceLimit(HeightResource,max_height);
791
252
  if (max_read >= 0)
792
0
    (void) SetMagickResourceLimit(ReadResource,max_read);
793
252
  if (max_write >= 0)
794
0
    (void) SetMagickResourceLimit(WriteResource,max_write);
795
252
  if (max_images >= 0)
796
0
    (void) SetMagickResourceLimit(ImagesResource,max_images);
797
252
}
798

799
/*
800
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801
%                                                                             %
802
%                                                                             %
803
%                                                                             %
804
%   L i b e r a t e M a g i c k R e s o u r c e                               %
805
%                                                                             %
806
%                                                                             %
807
%                                                                             %
808
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809
%
810
%  LiberateMagickResource() liberates resources of the specified type.
811
%
812
%  The format of the LiberateMagickResource() method is:
813
%
814
%      void LiberateMagickResource(const ResourceType type,
815
%        const magick_int64_t size)
816
%
817
%  A description of each parameter follows:
818
%
819
%    o type: The type of resource.
820
%
821
%    o size: The size of the resource.
822
%
823
%
824
*/
825
MagickExport void LiberateMagickResource(const ResourceType type,
826
  const magick_uint64_t size)
827
373M
{
828
373M
  ResourceInfo
829
373M
    *info;
830
831
373M
  if ((info=GetResourceInfo(type)))
832
373M
    {
833
373M
      magick_uint64_t
834
373M
        value=0;
835
836
373M
      switch(info->limit_type)
837
373M
        {
838
0
        case AbsoluteLimit:
839
0
          {
840
            /*
841
              Limit depends only on the currently requested size.
842
            */
843
0
            break;
844
0
          }
845
373M
        case SummationLimit:
846
373M
          {
847
            /*
848
              Limit depends on sum of previous allocations as well as
849
              the currently requested size.
850
            */
851
373M
            LockSemaphoreInfo(info->semaphore);
852
373M
            info->value-=size;
853
#if defined(DEBUG_MAGICK_RESOURCES) && DEBUG_MAGICK_RESOURCES
854
            assert(info->value >= info->minimum);
855
#endif /* if defined(DEBUG_MAGICK_RESOURCES) && DEBUG_MAGICK_RESOURCES */
856
373M
            value=info->value;
857
373M
            UnlockSemaphoreInfo(info->semaphore);
858
373M
            break;
859
0
          }
860
373M
        }
861
862
373M
      if (IsEventLogged(ResourceEvent))
863
0
        {
864
0
          char
865
0
            f_limit[MaxTextExtent],
866
0
            f_size[MaxTextExtent],
867
0
            f_value[MaxTextExtent];
868
869
0
          if (info->maximum == MagickResourceInfinity)
870
0
            {
871
0
              strlcpy(f_limit,"Unlimited",sizeof(f_limit));
872
0
            }
873
0
          else
874
0
            {
875
0
              FormatSize(info->maximum, f_limit);
876
0
              strlcat(f_limit,info->units,sizeof(f_limit));
877
0
            }
878
879
0
          FormatSize(size, f_size);
880
0
          strlcat(f_size,info->units,sizeof(f_size));
881
882
0
          if (info->limit_type == AbsoluteLimit)
883
0
            {
884
0
              strlcpy(f_value,"----",sizeof(f_value));
885
0
            }
886
0
          else
887
0
            {
888
0
              FormatSize(value, f_value);
889
0
              strlcat(f_value,info->units,sizeof(f_value));
890
0
            }
891
892
0
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
893
0
                                "%s %s%s/%s/%s",
894
0
                                info->name,
895
0
                                "-",
896
0
                                f_size,
897
0
                                f_value,
898
0
                                f_limit);
899
0
        }
900
373M
    }
901
373M
}
902

903
/*
904
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905
%                                                                             %
906
%                                                                             %
907
%                                                                             %
908
%  L i s t M a g i c k R e s o u r c e I n f o                                %
909
%                                                                             %
910
%                                                                             %
911
%                                                                             %
912
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913
%
914
%  Method ListMagickResourceInfo lists the resource info to a file.
915
%
916
%  The format of the ListMagickResourceInfo method is:
917
%
918
%      unsigned int ListMagickResourceInfo(FILE *file,ExceptionInfo *exception)
919
%
920
%  A description of each parameter follows.
921
%
922
%    o file:  An pointer to a FILE.
923
%
924
%    o exception: Return any errors or warnings in this structure.
925
%
926
%
927
*/
928
MagickExport MagickPassFail ListMagickResourceInfo(FILE *file,
929
  ExceptionInfo *exception)
930
0
{
931
0
  unsigned int
932
0
    index;
933
934
0
  ARG_NOT_USED(exception);
935
936
0
  if (file == (const FILE *) NULL)
937
0
    file=stdout;
938
939
0
  fprintf(file,"Resource Limits (Q%d, %d bits/pixel, %dbit address)\n",
940
0
          QuantumDepth,4*QuantumDepth,
941
0
          (sizeof(PixelPacket *) > 4 ? 64 : 32));
942
0
  fprintf(file,"----------------------------------------------------\n");
943
0
  for (index=1 ; index <= ResourceInfoMaxIndex; index++)
944
0
    {
945
0
      char
946
0
        environment[MaxTextExtent],
947
0
        heading[MaxTextExtent],
948
0
        limit[MaxTextExtent];
949
950
0
      LockSemaphoreInfo(resource_info[index].semaphore);
951
0
      if (resource_info[index].maximum == MagickResourceInfinity)
952
0
        {
953
0
          strlcpy(limit,"Unlimited",sizeof(limit));
954
0
        }
955
0
      else
956
0
        {
957
0
          FormatSize(resource_info[index].maximum,limit);
958
0
          strlcat(limit,resource_info[index].units,sizeof(limit));
959
0
        }
960
0
      FormatString(heading,"%c%s",toupper((int) resource_info[index].name[0]),
961
0
                   resource_info[index].name+1);
962
963
0
      (void) strlcpy(environment,resource_info[index].env,sizeof(environment));
964
965
0
      fprintf(file,"%8s: %10s (%s)\n", heading, limit, environment);
966
0
      UnlockSemaphoreInfo(resource_info[index].semaphore);
967
0
    }
968
0
  fprintf(file,
969
0
          "\n"
970
0
          "  IEC Binary Ranges:\n"
971
0
          "    Ki = \"kibi\" (2^10)\n"
972
0
          "    Mi = \"mebi\" (2^20)\n"
973
0
          "    Gi = \"gibi\" (2^30)\n"
974
0
          "    Ti = \"tebi\" (2^40)\n"
975
0
          "    Pi = \"pebi\" (2^50)\n"
976
0
          "    Ei = \"exbi\" (2^60)\n"
977
0
          );
978
979
0
  (void) fflush(file);
980
981
0
  return(MagickPass);
982
0
}
983
/*
984
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
985
%                                                                             %
986
%                                                                             %
987
%                                                                             %
988
%   S e t M a g i c k R e s o u r c e L i m i t                               %
989
%                                                                             %
990
%                                                                             %
991
%                                                                             %
992
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993
%
994
%  SetMagickResourceLimit() sets the limit for a particular resource.  The
995
%  units for resource types are as follows:
996
%
997
%    DiskResource    -- Bytes
998
%    FileResource    -- Open files
999
%    MapResource     -- Bytes
1000
%    MemoryResource  -- Bytes
1001
%    PixelsResource  -- Pixels
1002
%    ThreadsResource -- Threads
1003
%    WidthResource   -- Pixels
1004
%    HeightResource  -- Pixels
1005
%    ReadResource    -- Bytes
1006
%    WriteResource   -- Bytes
1007
%    ImagesResource  -- Discrete Images
1008
%
1009
%  The format of the SetMagickResourceLimit() method is:
1010
%
1011
%      void SetMagickResourceLimit(const ResourceType type,
1012
%        const unsigned long limit)
1013
%
1014
%  A description of each parameter follows:
1015
%
1016
%    o type: The type of resource.
1017
%
1018
%    o limit: The maximum limit for the resource.
1019
%
1020
%
1021
*/
1022
MagickExport MagickPassFail SetMagickResourceLimit(const ResourceType type,
1023
                                                   const magick_int64_t limit)
1024
156k
{
1025
156k
  ResourceInfo
1026
156k
    *info;
1027
1028
156k
  MagickPassFail
1029
156k
    status;
1030
1031
156k
  status=MagickFail;
1032
1033
156k
  if ((info=GetResourceInfo(type)))
1034
156k
    {
1035
156k
      LockSemaphoreInfo(info->semaphore);
1036
156k
      if (limit >= info->minimum)
1037
103k
        {
1038
103k
          char
1039
103k
            f_limit[MaxTextExtent];
1040
1041
1042
103k
          FormatSize((magick_int64_t) limit, f_limit);
1043
103k
          info->maximum = limit;
1044
          /* Cap highwater to new maximum */
1045
103k
          if (info->maximum < info->highwater)
1046
9
            info->highwater = info->maximum;
1047
#if defined(HAVE_OPENMP)
1048
          if (ThreadsResource == type)
1049
            omp_set_num_threads((int) limit);
1050
#endif /* HAVE_OPENMP */
1051
103k
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
1052
103k
                                "Set %s resource limit to %s%s",
1053
103k
                                info->name,f_limit,info->units);
1054
103k
          status=MagickPass;
1055
103k
        }
1056
52.4k
      else
1057
52.4k
        {
1058
52.4k
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
1059
52.4k
                                "Ignored bogus request to set %s resource limit"
1060
52.4k
                                " to %" MAGICK_INTMAX_F "d%s",
1061
52.4k
                                info->name,limit,info->units);
1062
52.4k
        }
1063
156k
      UnlockSemaphoreInfo(info->semaphore);
1064
156k
    }
1065
1066
156k
  return(status);
1067
156k
}
1068
1069
/*
1070
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071
%                                                                             %
1072
%                                                                             %
1073
%                                                                             %
1074
+   _UpdateMagickResourceHighwater                                            %
1075
%                                                                             %
1076
%                                                                             %
1077
%                                                                             %
1078
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079
%
1080
%  _UpdateMagickResourceHighwater() updates the highwater for the specified
1081
%  resource.  The highwater value is only updated if it is greater than the
1082
%  minimum value for the resource, less than the current maximum for the
1083
%  resource, and greater than the current highwater value for the resource.
1084
%  The intention is that highwater values are only updated for resources
1085
%  successfully used rather than for resources requested.
1086
%
1087
%  The format of the _UpdateMagickResourceHighwater() method is:
1088
%
1089
%      MagickPassFail _UpdateMagickResourceHighwater(const ResourceType type,
1090
%        const unsigned long limit)
1091
%
1092
%  A description of each parameter follows:
1093
%
1094
%    o type: The type of resource.
1095
%
1096
%    o limit: The new proposed highwater for the resource.
1097
%
1098
%
1099
*/
1100
extern MagickPassFail _UpdateMagickResourceHighwater(const ResourceType type,
1101
                                                     const magick_int64_t highwater)
1102
10.8M
{
1103
10.8M
  ResourceInfo
1104
10.8M
    *info;
1105
1106
10.8M
  MagickPassFail
1107
10.8M
    status;
1108
1109
10.8M
  status=MagickFail;
1110
1111
10.8M
  if ((info=GetResourceInfo(type)))
1112
10.8M
    {
1113
10.8M
      LockSemaphoreInfo(info->semaphore);
1114
10.8M
      if ((highwater > info->minimum) && (highwater <= info->maximum)
1115
128k
          && (highwater > info->highwater))
1116
19.8k
        {
1117
19.8k
          char
1118
19.8k
            f_highwater[MaxTextExtent];
1119
1120
1121
19.8k
          FormatSize((magick_int64_t) highwater, f_highwater);
1122
19.8k
          info->highwater = highwater;
1123
19.8k
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
1124
19.8k
                                "Updated %s resource highwater to %s%s",
1125
19.8k
                                info->name,f_highwater,info->units);
1126
19.8k
          status=MagickPass;
1127
19.8k
        }
1128
10.8M
      else
1129
10.8M
        {
1130
10.8M
          (void) LogMagickEvent(ResourceEvent,GetMagickModule(),
1131
10.8M
                                "Ignored request to set %s highwater to"
1132
10.8M
                                " %" MAGICK_INTMAX_F "d%s",
1133
10.8M
                                info->name,highwater,info->units);
1134
10.8M
        }
1135
10.8M
      UnlockSemaphoreInfo(info->semaphore);
1136
10.8M
    }
1137
1138
10.8M
  return(status);
1139
10.8M
}