PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llcommon/llapr.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 779 lines | 582 code | 105 blank | 92 comment | 83 complexity | e709c2d110d0c1ea71e57cbee84340c3 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llapr.cpp
  3. * @author Phoenix
  4. * @date 2004-11-28
  5. * @brief Helper functions for using the apache portable runtime library.
  6. *
  7. * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "llapr.h"
  30. #include "apr_dso.h"
  31. apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
  32. LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool.
  33. apr_thread_mutex_t *gLogMutexp = NULL;
  34. apr_thread_mutex_t *gCallStacksLogMutexp = NULL;
  35. const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
  36. void ll_init_apr()
  37. {
  38. if (!gAPRPoolp)
  39. {
  40. // Initialize APR and create the global pool
  41. apr_initialize();
  42. apr_pool_create(&gAPRPoolp, NULL);
  43. // Initialize the logging mutex
  44. apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
  45. apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
  46. }
  47. if(!LLAPRFile::sAPRFilePoolp)
  48. {
  49. LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ;
  50. }
  51. }
  52. void ll_cleanup_apr()
  53. {
  54. LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
  55. if (gLogMutexp)
  56. {
  57. // Clean up the logging mutex
  58. // All other threads NEED to be done before we clean up APR, so this is okay.
  59. apr_thread_mutex_destroy(gLogMutexp);
  60. gLogMutexp = NULL;
  61. }
  62. if (gCallStacksLogMutexp)
  63. {
  64. // Clean up the logging mutex
  65. // All other threads NEED to be done before we clean up APR, so this is okay.
  66. apr_thread_mutex_destroy(gCallStacksLogMutexp);
  67. gCallStacksLogMutexp = NULL;
  68. }
  69. if (gAPRPoolp)
  70. {
  71. apr_pool_destroy(gAPRPoolp);
  72. gAPRPoolp = NULL;
  73. }
  74. if (LLAPRFile::sAPRFilePoolp)
  75. {
  76. delete LLAPRFile::sAPRFilePoolp ;
  77. LLAPRFile::sAPRFilePoolp = NULL ;
  78. }
  79. apr_terminate();
  80. }
  81. //
  82. //
  83. //LLAPRPool
  84. //
  85. LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
  86. : mParent(parent),
  87. mReleasePoolFlag(releasePoolFlag),
  88. mMaxSize(size),
  89. mPool(NULL)
  90. {
  91. createAPRPool() ;
  92. }
  93. LLAPRPool::~LLAPRPool()
  94. {
  95. releaseAPRPool() ;
  96. }
  97. void LLAPRPool::createAPRPool()
  98. {
  99. if(mPool)
  100. {
  101. return ;
  102. }
  103. mStatus = apr_pool_create(&mPool, mParent);
  104. ll_apr_warn_status(mStatus) ;
  105. if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes.
  106. {
  107. apr_allocator_t *allocator = apr_pool_allocator_get(mPool);
  108. if (allocator)
  109. {
  110. apr_allocator_max_free_set(allocator, mMaxSize) ;
  111. }
  112. }
  113. }
  114. void LLAPRPool::releaseAPRPool()
  115. {
  116. if(!mPool)
  117. {
  118. return ;
  119. }
  120. if(!mParent || mReleasePoolFlag)
  121. {
  122. apr_pool_destroy(mPool) ;
  123. mPool = NULL ;
  124. }
  125. }
  126. //virtual
  127. apr_pool_t* LLAPRPool::getAPRPool()
  128. {
  129. return mPool ;
  130. }
  131. LLVolatileAPRPool::LLVolatileAPRPool(BOOL is_local, apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
  132. : LLAPRPool(parent, size, releasePoolFlag),
  133. mNumActiveRef(0),
  134. mNumTotalRef(0),
  135. mMutexPool(NULL),
  136. mMutexp(NULL)
  137. {
  138. //create mutex
  139. if(!is_local) //not a local apr_pool, that is: shared by multiple threads.
  140. {
  141. apr_pool_create(&mMutexPool, NULL); // Create a pool for mutex
  142. apr_thread_mutex_create(&mMutexp, APR_THREAD_MUTEX_UNNESTED, mMutexPool);
  143. }
  144. }
  145. LLVolatileAPRPool::~LLVolatileAPRPool()
  146. {
  147. //delete mutex
  148. if(mMutexp)
  149. {
  150. apr_thread_mutex_destroy(mMutexp);
  151. apr_pool_destroy(mMutexPool);
  152. }
  153. }
  154. //
  155. //define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
  156. //
  157. //virtual
  158. apr_pool_t* LLVolatileAPRPool::getAPRPool()
  159. {
  160. return LLVolatileAPRPool::getVolatileAPRPool() ;
  161. }
  162. apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
  163. {
  164. LLScopedLock lock(mMutexp) ;
  165. mNumTotalRef++ ;
  166. mNumActiveRef++ ;
  167. if(!mPool)
  168. {
  169. createAPRPool() ;
  170. }
  171. return mPool ;
  172. }
  173. void LLVolatileAPRPool::clearVolatileAPRPool()
  174. {
  175. LLScopedLock lock(mMutexp) ;
  176. if(mNumActiveRef > 0)
  177. {
  178. mNumActiveRef--;
  179. if(mNumActiveRef < 1)
  180. {
  181. if(isFull())
  182. {
  183. mNumTotalRef = 0 ;
  184. //destroy the apr_pool.
  185. releaseAPRPool() ;
  186. }
  187. else
  188. {
  189. //This does not actually free the memory,
  190. //it just allows the pool to re-use this memory for the next allocation.
  191. apr_pool_clear(mPool) ;
  192. }
  193. }
  194. }
  195. else
  196. {
  197. llassert_always(mNumActiveRef > 0) ;
  198. }
  199. //paranoia check if the pool is jammed.
  200. //will remove the check before going to release.
  201. llassert_always(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
  202. }
  203. BOOL LLVolatileAPRPool::isFull()
  204. {
  205. return mNumTotalRef > FULL_VOLATILE_APR_POOL ;
  206. }
  207. //---------------------------------------------------------------------
  208. //
  209. // LLScopedLock
  210. //
  211. LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex)
  212. {
  213. if(mutex)
  214. {
  215. if(ll_apr_warn_status(apr_thread_mutex_lock(mMutex)))
  216. {
  217. mLocked = false;
  218. }
  219. else
  220. {
  221. mLocked = true;
  222. }
  223. }
  224. else
  225. {
  226. mLocked = false;
  227. }
  228. }
  229. LLScopedLock::~LLScopedLock()
  230. {
  231. unlock();
  232. }
  233. void LLScopedLock::unlock()
  234. {
  235. if(mLocked)
  236. {
  237. if(!ll_apr_warn_status(apr_thread_mutex_unlock(mMutex)))
  238. {
  239. mLocked = false;
  240. }
  241. }
  242. }
  243. //---------------------------------------------------------------------
  244. bool ll_apr_warn_status(apr_status_t status)
  245. {
  246. if(APR_SUCCESS == status) return false;
  247. char buf[MAX_STRING]; /* Flawfinder: ignore */
  248. apr_strerror(status, buf, sizeof(buf));
  249. LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
  250. return true;
  251. }
  252. bool ll_apr_warn_status(apr_status_t status, apr_dso_handle_t *handle)
  253. {
  254. bool result = ll_apr_warn_status(status);
  255. // Despite observed truncation of actual Mac dylib load errors, increasing
  256. // this buffer to more than MAX_STRING doesn't help: it appears that APR
  257. // stores the output in a fixed 255-character internal buffer. (*sigh*)
  258. char buf[MAX_STRING]; /* Flawfinder: ignore */
  259. apr_dso_error(handle, buf, sizeof(buf));
  260. LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
  261. return result;
  262. }
  263. void ll_apr_assert_status(apr_status_t status)
  264. {
  265. llassert(! ll_apr_warn_status(status));
  266. }
  267. void ll_apr_assert_status(apr_status_t status, apr_dso_handle_t *handle)
  268. {
  269. llassert(! ll_apr_warn_status(status, handle));
  270. }
  271. //---------------------------------------------------------------------
  272. //
  273. // LLAPRFile functions
  274. //
  275. LLAPRFile::LLAPRFile()
  276. : mFile(NULL),
  277. mCurrentFilePoolp(NULL)
  278. {
  279. }
  280. LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool)
  281. : mFile(NULL),
  282. mCurrentFilePoolp(NULL)
  283. {
  284. open(filename, flags, pool);
  285. }
  286. LLAPRFile::~LLAPRFile()
  287. {
  288. close() ;
  289. }
  290. apr_status_t LLAPRFile::close()
  291. {
  292. apr_status_t ret = APR_SUCCESS ;
  293. if(mFile)
  294. {
  295. ret = apr_file_close(mFile);
  296. mFile = NULL ;
  297. }
  298. if(mCurrentFilePoolp)
  299. {
  300. mCurrentFilePoolp->clearVolatileAPRPool() ;
  301. mCurrentFilePoolp = NULL ;
  302. }
  303. return ret ;
  304. }
  305. apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep)
  306. {
  307. apr_status_t s ;
  308. //check if already open some file
  309. llassert_always(!mFile) ;
  310. llassert_always(!mCurrentFilePoolp) ;
  311. apr_pool_t* apr_pool = pool ? pool->getVolatileAPRPool() : NULL ;
  312. s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, getAPRFilePool(apr_pool));
  313. if (s != APR_SUCCESS || !mFile)
  314. {
  315. mFile = NULL ;
  316. if (sizep)
  317. {
  318. *sizep = 0;
  319. }
  320. }
  321. else if (sizep)
  322. {
  323. S32 file_size = 0;
  324. apr_off_t offset = 0;
  325. if (apr_file_seek(mFile, APR_END, &offset) == APR_SUCCESS)
  326. {
  327. llassert_always(offset <= 0x7fffffff);
  328. file_size = (S32)offset;
  329. offset = 0;
  330. apr_file_seek(mFile, APR_SET, &offset);
  331. }
  332. *sizep = file_size;
  333. }
  334. if(!mCurrentFilePoolp)
  335. {
  336. mCurrentFilePoolp = pool ;
  337. if(!mFile)
  338. {
  339. close() ;
  340. }
  341. }
  342. return s ;
  343. }
  344. //use gAPRPoolp.
  345. apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool)
  346. {
  347. apr_status_t s;
  348. //check if already open some file
  349. llassert_always(!mFile) ;
  350. llassert_always(!mCurrentFilePoolp) ;
  351. llassert_always(use_global_pool) ; //be aware of using gAPRPoolp.
  352. s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
  353. if (s != APR_SUCCESS || !mFile)
  354. {
  355. mFile = NULL ;
  356. close() ;
  357. return s;
  358. }
  359. return s;
  360. }
  361. apr_pool_t* LLAPRFile::getAPRFilePool(apr_pool_t* pool)
  362. {
  363. if(!pool)
  364. {
  365. mCurrentFilePoolp = sAPRFilePoolp ;
  366. return mCurrentFilePoolp->getVolatileAPRPool() ;
  367. }
  368. return pool ;
  369. }
  370. // File I/O
  371. S32 LLAPRFile::read(void *buf, S32 nbytes)
  372. {
  373. if(!mFile)
  374. {
  375. llwarns << "apr mFile is removed by somebody else. Can not read." << llendl ;
  376. return 0;
  377. }
  378. apr_size_t sz = nbytes;
  379. apr_status_t s = apr_file_read(mFile, buf, &sz);
  380. if (s != APR_SUCCESS)
  381. {
  382. ll_apr_warn_status(s);
  383. return 0;
  384. }
  385. else
  386. {
  387. llassert_always(sz <= 0x7fffffff);
  388. return (S32)sz;
  389. }
  390. }
  391. S32 LLAPRFile::write(const void *buf, S32 nbytes)
  392. {
  393. if(!mFile)
  394. {
  395. llwarns << "apr mFile is removed by somebody else. Can not write." << llendl ;
  396. return 0;
  397. }
  398. apr_size_t sz = nbytes;
  399. apr_status_t s = apr_file_write(mFile, buf, &sz);
  400. if (s != APR_SUCCESS)
  401. {
  402. ll_apr_warn_status(s);
  403. return 0;
  404. }
  405. else
  406. {
  407. llassert_always(sz <= 0x7fffffff);
  408. return (S32)sz;
  409. }
  410. }
  411. S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
  412. {
  413. return LLAPRFile::seek(mFile, where, offset) ;
  414. }
  415. //
  416. //*******************************************************************************************************************************
  417. //static components of LLAPRFile
  418. //
  419. //static
  420. apr_status_t LLAPRFile::close(apr_file_t* file_handle, LLVolatileAPRPool* pool)
  421. {
  422. apr_status_t ret = APR_SUCCESS ;
  423. if(file_handle)
  424. {
  425. ret = apr_file_close(file_handle);
  426. file_handle = NULL ;
  427. }
  428. if(pool)
  429. {
  430. pool->clearVolatileAPRPool() ;
  431. }
  432. return ret ;
  433. }
  434. //static
  435. apr_file_t* LLAPRFile::open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
  436. {
  437. apr_status_t s;
  438. apr_file_t* file_handle ;
  439. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  440. s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool());
  441. if (s != APR_SUCCESS || !file_handle)
  442. {
  443. ll_apr_warn_status(s);
  444. LL_WARNS("APR") << " Attempting to open filename: " << filename << LL_ENDL;
  445. file_handle = NULL ;
  446. close(file_handle, pool) ;
  447. return NULL;
  448. }
  449. return file_handle ;
  450. }
  451. //static
  452. S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
  453. {
  454. if(!file_handle)
  455. {
  456. return -1 ;
  457. }
  458. apr_status_t s;
  459. apr_off_t apr_offset;
  460. if (offset >= 0)
  461. {
  462. apr_offset = (apr_off_t)offset;
  463. s = apr_file_seek(file_handle, where, &apr_offset);
  464. }
  465. else
  466. {
  467. apr_offset = 0;
  468. s = apr_file_seek(file_handle, APR_END, &apr_offset);
  469. }
  470. if (s != APR_SUCCESS)
  471. {
  472. ll_apr_warn_status(s);
  473. return -1;
  474. }
  475. else
  476. {
  477. llassert_always(apr_offset <= 0x7fffffff);
  478. return (S32)apr_offset;
  479. }
  480. }
  481. //static
  482. S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
  483. {
  484. //*****************************************
  485. apr_file_t* file_handle = open(filename, pool, APR_READ|APR_BINARY);
  486. //*****************************************
  487. if (!file_handle)
  488. {
  489. return 0;
  490. }
  491. llassert(offset >= 0);
  492. if (offset > 0)
  493. offset = LLAPRFile::seek(file_handle, APR_SET, offset);
  494. apr_size_t bytes_read;
  495. if (offset < 0)
  496. {
  497. bytes_read = 0;
  498. }
  499. else
  500. {
  501. bytes_read = nbytes ;
  502. apr_status_t s = apr_file_read(file_handle, buf, &bytes_read);
  503. if (s != APR_SUCCESS)
  504. {
  505. LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL;
  506. ll_apr_warn_status(s);
  507. bytes_read = 0;
  508. }
  509. else
  510. {
  511. llassert_always(bytes_read <= 0x7fffffff);
  512. }
  513. }
  514. //*****************************************
  515. close(file_handle, pool) ;
  516. //*****************************************
  517. return (S32)bytes_read;
  518. }
  519. //static
  520. S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
  521. {
  522. apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
  523. if (offset < 0)
  524. {
  525. flags |= APR_APPEND;
  526. offset = 0;
  527. }
  528. //*****************************************
  529. apr_file_t* file_handle = open(filename, pool, flags);
  530. //*****************************************
  531. if (!file_handle)
  532. {
  533. return 0;
  534. }
  535. if (offset > 0)
  536. {
  537. offset = LLAPRFile::seek(file_handle, APR_SET, offset);
  538. }
  539. apr_size_t bytes_written;
  540. if (offset < 0)
  541. {
  542. bytes_written = 0;
  543. }
  544. else
  545. {
  546. bytes_written = nbytes ;
  547. apr_status_t s = apr_file_write(file_handle, buf, &bytes_written);
  548. if (s != APR_SUCCESS)
  549. {
  550. LL_WARNS("APR") << " Attempting to write filename: " << filename << LL_ENDL;
  551. ll_apr_warn_status(s);
  552. bytes_written = 0;
  553. }
  554. else
  555. {
  556. llassert_always(bytes_written <= 0x7fffffff);
  557. }
  558. }
  559. //*****************************************
  560. LLAPRFile::close(file_handle, pool);
  561. //*****************************************
  562. return (S32)bytes_written;
  563. }
  564. //static
  565. bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
  566. {
  567. apr_status_t s;
  568. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  569. s = apr_file_remove(filename.c_str(), pool->getVolatileAPRPool());
  570. pool->clearVolatileAPRPool() ;
  571. if (s != APR_SUCCESS)
  572. {
  573. ll_apr_warn_status(s);
  574. LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL;
  575. return false;
  576. }
  577. return true;
  578. }
  579. //static
  580. bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool)
  581. {
  582. apr_status_t s;
  583. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  584. s = apr_file_rename(filename.c_str(), newname.c_str(), pool->getVolatileAPRPool());
  585. pool->clearVolatileAPRPool() ;
  586. if (s != APR_SUCCESS)
  587. {
  588. ll_apr_warn_status(s);
  589. LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL;
  590. return false;
  591. }
  592. return true;
  593. }
  594. //static
  595. bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
  596. {
  597. apr_file_t* apr_file;
  598. apr_status_t s;
  599. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  600. s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool());
  601. if (s != APR_SUCCESS || !apr_file)
  602. {
  603. pool->clearVolatileAPRPool() ;
  604. return false;
  605. }
  606. else
  607. {
  608. apr_file_close(apr_file) ;
  609. pool->clearVolatileAPRPool() ;
  610. return true;
  611. }
  612. }
  613. //static
  614. S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool)
  615. {
  616. apr_file_t* apr_file;
  617. apr_finfo_t info;
  618. apr_status_t s;
  619. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  620. s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool->getVolatileAPRPool());
  621. if (s != APR_SUCCESS || !apr_file)
  622. {
  623. pool->clearVolatileAPRPool() ;
  624. return 0;
  625. }
  626. else
  627. {
  628. apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);
  629. apr_file_close(apr_file) ;
  630. pool->clearVolatileAPRPool() ;
  631. if (s == APR_SUCCESS)
  632. {
  633. return (S32)info.size;
  634. }
  635. else
  636. {
  637. return 0;
  638. }
  639. }
  640. }
  641. //static
  642. bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
  643. {
  644. apr_status_t s;
  645. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  646. s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool->getVolatileAPRPool());
  647. pool->clearVolatileAPRPool() ;
  648. if (s != APR_SUCCESS)
  649. {
  650. ll_apr_warn_status(s);
  651. LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
  652. return false;
  653. }
  654. return true;
  655. }
  656. //static
  657. bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
  658. {
  659. apr_status_t s;
  660. pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
  661. s = apr_file_remove(dirname.c_str(), pool->getVolatileAPRPool());
  662. pool->clearVolatileAPRPool() ;
  663. if (s != APR_SUCCESS)
  664. {
  665. ll_apr_warn_status(s);
  666. LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL;
  667. return false;
  668. }
  669. return true;
  670. }
  671. //
  672. //end of static components of LLAPRFile
  673. //*******************************************************************************************************************************
  674. //