diff --git a/sites/all/modules/memcache/LICENSE.txt b/sites/all/modules/memcache/LICENSE.txt
index 2c095c8d3f42488e8168f9710a4ffbfc4125a159..d159169d1050894d3ea3b98e1c965c4058208fe1 100644
--- a/sites/all/modules/memcache/LICENSE.txt
+++ b/sites/all/modules/memcache/LICENSE.txt
@@ -1,274 +1,339 @@
-GNU GENERAL PUBLIC LICENSE
-
-              Version 2, June 1991
-
-Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
-Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
-verbatim copies of this license document, but changing it is not allowed.
-
-                  Preamble
-
-The licenses for most software are designed to take away your freedom to
-share and change it. By contrast, the GNU General Public License is
-intended to guarantee your freedom to share and change free software--to
-make sure the software is free for all its users. This General Public License
-applies to most of the Free Software Foundation's software and to any other
-program whose authors commit to using it. (Some other Free Software
-Foundation software is covered by the GNU Library General Public License
-instead.) You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our
-General Public Licenses are designed to make sure that you have the
-freedom to distribute copies of free software (and charge for this service if
-you wish), that you receive source code or can get it if you want it, that you
-can change the software or use pieces of it in new free programs; and that
-you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to
-deny you these rights or to ask you to surrender the rights. These restrictions
-translate to certain responsibilities for you if you distribute copies of the
-software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or for
-a fee, you must give the recipients all the rights that you have. You must make
-sure that they, too, receive or can get the source code. And you must show
-them these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2)
-offer you this license which gives you legal permission to copy, distribute
-and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that
-everyone understands that there is no warranty for this free software. If the
-software is modified by someone else and passed on, we want its recipients
-to know that what they have is not the original, so that any problems
-introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We
-wish to avoid the danger that redistributors of a free program will individually
-obtain patent licenses, in effect making the program proprietary. To prevent
-this, we have made it clear that any patent must be licensed for everyone's
-free use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification
-follow.
-
-           GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
-               MODIFICATION
-
-0. This License applies to any program or other work which contains a notice
-placed by the copyright holder saying it may be distributed under the terms
-of this General Public License. The "Program", below, refers to any such
-program or work, and a "work based on the Program" means either the
-Program or any derivative work under copyright law: that is to say, a work
-containing the Program or a portion of it, either verbatim or with
-modifications and/or translated into another language. (Hereinafter, translation
-is included without limitation in the term "modification".) Each licensee is
-addressed as "you".
-
-Activities other than copying, distribution and modification are not covered
-by this License; they are outside its scope. The act of running the Program is
-not restricted, and the output from the Program is covered only if its contents
-constitute a work based on the Program (independent of having been made
-by running the Program). Whether that is true depends on what the Program
-does.
-
-1. You may copy and distribute verbatim copies of the Program's source
-code as you receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice and
-disclaimer of warranty; keep intact all the notices that refer to this License
-and to the absence of any warranty; and give any other recipients of the
-Program a copy of this License along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and you
-may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it,
-thus forming a work based on the Program, and copy and distribute such
-modifications or work under the terms of Section 1 above, provided that you
-also meet all of these conditions:
-
-a) You must cause the modified files to carry prominent notices stating that
-you changed the files and the date of any change.
-
-b) You must cause any work that you distribute or publish, that in whole or in
-part contains or is derived from the Program or any part thereof, to be
-licensed as a whole at no charge to all third parties under the terms of this
-License.
-
-c) If the modified program normally reads commands interactively when run,
-you must cause it, when started running for such interactive use in the most
-ordinary way, to print or display an announcement including an appropriate
-copyright notice and a notice that there is no warranty (or else, saying that
-you provide a warranty) and that users may redistribute the program under
-these conditions, and telling the user how to view a copy of this License.
-(Exception: if the Program itself is interactive but does not normally print such
-an announcement, your work based on the Program is not required to print
-an announcement.)
-
-These requirements apply to the modified work as a whole. If identifiable
-sections of that work are not derived from the Program, and can be
-reasonably considered independent and separate works in themselves, then
-this License, and its terms, do not apply to those sections when you distribute
-them as separate works. But when you distribute the same sections as part
-of a whole which is a work based on the Program, the distribution of the
-whole must be on the terms of this License, whose permissions for other
-licensees extend to the entire whole, and thus to each and every part
-regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to
-work written entirely by you; rather, the intent is to exercise the right to
-control the distribution of derivative or collective works based on the
-Program.
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
 
 In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of a
-storage or distribution medium does not bring the other work under the scope
-of this License.
-
-3. You may copy and distribute the Program (or a work based on it, under
-Section 2) in object code or executable form under the terms of Sections 1
-and 2 above provided that you also do one of the following:
-
-a) Accompany it with the complete corresponding machine-readable source
-code, which must be distributed under the terms of Sections 1 and 2 above
-on a medium customarily used for software interchange; or,
-
-b) Accompany it with a written offer, valid for at least three years, to give
-any third party, for a charge no more than your cost of physically performing
-source distribution, a complete machine-readable copy of the corresponding
-source code, to be distributed under the terms of Sections 1 and 2 above on
-a medium customarily used for software interchange; or,
-
-c) Accompany it with the information you received as to the offer to distribute
-corresponding source code. (This alternative is allowed only for
-noncommercial distribution and only if you received the program in object
-code or executable form with such an offer, in accord with Subsection b
-above.)
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
 
 The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source code
-means all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation and
-installation of the executable. However, as a special exception, the source
-code distributed need not include anything that is normally distributed (in
-either source or binary form) with the major components (compiler, kernel,
-and so on) of the operating system on which the executable runs, unless that
-component itself accompanies the executable.
-
-If distribution of executable or object code is made by offering access to
-copy from a designated place, then offering equivalent access to copy the
-source code from the same place counts as distribution of the source code,
-even though third parties are not compelled to copy the source along with the
-object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as
-expressly provided under this License. Any attempt otherwise to copy,
-modify, sublicense or distribute the Program is void, and will automatically
-terminate your rights under this License. However, parties who have received
-copies, or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-5. You are not required to accept this License, since you have not signed it.
-However, nothing else grants you permission to modify or distribute the
-Program or its derivative works. These actions are prohibited by law if you
-do not accept this License. Therefore, by modifying or distributing the
-Program (or any work based on the Program), you indicate your acceptance
-of this License to do so, and all its terms and conditions for copying,
-distributing or modifying the Program or works based on it.
-
-6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the original
-licensor to copy, distribute or modify the Program subject to these terms and
-conditions. You may not impose any further restrictions on the recipients'
-exercise of the rights granted herein. You are not responsible for enforcing
-compliance by third parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues), conditions
-are imposed on you (whether by court order, agreement or otherwise) that
-contradict the conditions of this License, they do not excuse you from the
-conditions of this License. If you cannot distribute so as to satisfy
-simultaneously your obligations under this License and any other pertinent
-obligations, then as a consequence you may not distribute the Program at all.
-For example, if a patent license would not permit royalty-free redistribution
-of the Program by all those who receive copies directly or indirectly through
-you, then the only way you could satisfy both it and this License would be to
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
 refrain entirely from distribution of the Program.
 
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply and
-the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or
-other property right claims or to contest validity of any such claims; this
-section has the sole purpose of protecting the integrity of the free software
-distribution system, which is implemented by public license practices. Many
-people have made generous contributions to the wide range of software
-distributed through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing to
-distribute software through any other system and a licensee cannot impose
-that choice.
-
-This section is intended to make thoroughly clear what is believed to be a
-consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain
-countries either by patents or by copyrighted interfaces, the original copyright
-holder who places the Program under this License may add an explicit
-geographical distribution limitation excluding those countries, so that
-distribution is permitted only in or among countries not thus excluded. In such
-case, this License incorporates the limitation as if written in the body of this
-License.
-
-9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will be
-similar in spirit to the present version, but may differ in detail to address new
-problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies
-a version number of this License which applies to it and "any later version",
-you have the option of following the terms and conditions either of that
-version or of any later version published by the Free Software Foundation. If
-the Program does not specify a version number of this License, you may
-choose any version ever published by the Free Software Foundation.
-
-10. If you wish to incorporate parts of the Program into other free programs
-whose distribution conditions are different, write to the author to ask for
-permission. For software which is copyrighted by the Free Software
-Foundation, write to the Free Software Foundation; we sometimes make
-exceptions for this. Our decision will be guided by the two goals of
-preserving the free status of all derivatives of our free software and of
-promoting the sharing and reuse of software generally.
-
-               NO WARRANTY
-
-11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
-PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
-STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
-WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
-INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
-PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
-NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
-AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
-ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
-LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
-SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
-ARISING OUT OF THE USE OR INABILITY TO USE THE
-PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
-OR DATA BEING RENDERED INACCURATE OR LOSSES
-SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
-PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
-IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
-THE POSSIBILITY OF SUCH DAMAGES.
-
-          END OF TERMS AND CONDITIONS
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/sites/all/modules/memcache/README.txt b/sites/all/modules/memcache/README.txt
index 4efd86d3880e82e852d7062b867c6ce6a1efa4f7..592983843cc5c025ccbddccb7eb2d50f9f2332e1 100644
--- a/sites/all/modules/memcache/README.txt
+++ b/sites/all/modules/memcache/README.txt
@@ -4,49 +4,35 @@
 - PHP 5.1 or greater
 - Availability of a memcached daemon: http://memcached.org/
 - One of the two PECL memcache packages:
-  - http://pecl.php.net/package/memcache
-  - http://pecl.php.net/package/memcached (recommended
+  - http://pecl.php.net/package/memcache (recommended):
+  - http://pecl.php.net/package/memcached
 
 ## INSTALLATION ##
 
 These are the broad steps you need to take in order to use this software. Order
 is important.
 
-1. Install the memcached binaries on your server. See 
-
-http://www.lullabot.com/articles/how_install_memcache_debian_etch
-
-2. Install the PECL memcache extension for PHP. This must be version 2.2.1 or 
-   higher or you will experience errors.
-3. Put your site into offline mode.
-4. Download and install the memcache module.
-5. If you have previously been running the memcache module, run update.php.
-6. Start at least one instance of memcached on your server.
-7. Edit settings.php to configure the servers, clusters and bins that memcache
-   is supposed to use.
-8. Edit settings.php to include cache.inc, memcache.inc.  For example:
-     include_once('./includes/cache.inc');
-     include_once('./sites/all/modules/memcache/memcache.inc');
-9. Edit settings.php to make memcache the default caching class:
-     $conf['cache_default_class'] = 'MemCacheDrupal';
-9. Bring your site back online.
+ 1. Install the memcached binaries on your server. See for instance:
+      http://www.lullabot.com/articles/how_install_memcache_debian_etch
+ 2. Install the PECL memcache extension for PHP. This must be version 2.2.1 or 
+    higher or you will experience errors.
+ 3. Put your site into offline mode.
+ 4. Download and install the memcache module.
+ 5. If you have previously been running the memcache module, run update.php.
+ 6. Start at least one instance of memcached on your server.
+ 7. Edit settings.php to configure the servers, clusters and bins that memcache
+    is supposed to use.
+ 8. Edit settings.php to make memcache the default cache class, for example:
+      $conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
+      $conf['cache_default_class'] = 'MemCacheDrupal';
+ 9. Make sure the following line also exists, to ensure that the special
+    cache_form bin is assigned to non-volatile storage:
+      $conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
+10. Bring your site back online.
 
 For instructions on 1 and 2 above, please see the INSTALLATION.txt file that
 comes with the memcache module download.
 
-http://www.lullabot.com/files/memcache-inc.png
-
-In Drupal 7+, you no longer should set cache_inc in settings.php.  Instead, you
-will have to manually include 'cache.inc' and 'memcache.inc', then update $conf
-to tell Drupal to default to memcache for caching:
-
-  // the path to the core cache file
-  include_once('./includes/cache.inc');
-  // the path to the memcache cache file
-  include_once('./sites/all/modules/memcache/memcache.inc');
-  // make MemCacheDrupal the default cache class
-  $conf['cache_default_class'] = 'MemCacheDrupal';
-
 ## SERVERS ##
 
 If you want the simple version, you can start one default memcache instance on
@@ -79,7 +65,7 @@ The bin/cluster/server model can be described as follows:
 - Servers are memcached instances identified by host:port.
 
 - Bins are groups of data that get cached together and map 1:1 to the $table
-  param in cache_set(). Examples from Drupal core are cache_filter,
+  parameter of cache_set(). Examples from Drupal core are cache_filter,
   cache_menu. The default is 'cache'.
 
 - Clusters are groups of servers that act as a memory pool.
@@ -106,11 +92,13 @@ $conf = array(
 
 Here is an example configuration that has two clusters, 'default' and
 'cluster2'. Five memcached instances are divided up between the two
-clusters. 'cache_filter' and 'cache_menu' bins goe to 'cluster2'. All other
+clusters. 'cache_filter' and 'cache_menu' bins go to 'cluster2'. All other
 bins go to 'default'.
 
-include_once('./includes/cache.inc');
-include_once('./sites/all/modules/memcache/memcache.inc');
+$conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
+$conf['cache_default_class'] = 'MemCacheDrupal';
+// The 'cache_form' bin must be assigned no non-volatile storage.
+$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
 $conf = array(
   'cache_default_class' = 'MemCacheDrupal',
   'memcache_servers' => array('localhost:11211' => 'default',
@@ -123,6 +111,7 @@ $conf = array(
                            'cache_filter' => 'cluster2',
                            'cache_menu' => 'cluster2'),
 );
+
 ## PREFIXING ##
 
 If you want to have multiple Drupal installations share memcached instances,
@@ -136,13 +125,16 @@ $conf = array(
 
 ## SESSIONS ##
 
+NOTE: Session.inc is not yet ported to Drupal 7 and is not recommended for use
+in production..
+
 Here is a sample config that uses memcache for sessions. Note you MUST have
 a session and a users server set up for memcached sessions to work.
 
-NOTE: Session.inc is not yet ported to Drupal 7.
-
-include_once('./includes/cache.inc');
-include_once('./sites/all/modules/memcache/memcache.inc');
+$conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
+$conf['cache_default_class'] = 'MemCacheDrupal';
+// The 'cache_form' bin must be assigned no non-volatile storage.
+$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
 $conf = array(
   'cache_default_class' = 'MemCacheDrupal',
   'session_inc' => './sites/all/modules/memcache/memcache-session.inc',
@@ -183,12 +175,11 @@ See http://drupal.org/node/273824
 A module offering a UI for memcache is included. It provides stats, a
 way to clear the cache, and an interface to organize servers/bins/clusters.
 
-
 ## Memcached PECL Extension Support
 
-We also now support the Memcached PECL extension. If you install this extension,
-it will be used by default. This new extension backends to libmemcached and 
-allows you to use some of the newer advanced features in memcached 1.4. 
+We also now support the Memcached PECL extension. This new extension backends
+to libmemcached and allows you to use some of the newer advanced features in
+memcached 1.4. 
 
 NOTE: It is important to realize that the memcache php.ini options do not impact
 the memcached extension, this new extension doesn't read in options that way.
diff --git a/sites/all/modules/memcache/dmemcache.inc b/sites/all/modules/memcache/dmemcache.inc
index df3c85dc48890962876ff2d03c26889ac1a0a549..5b0629a4a59aadac55ac1e6d517b2b09326c8dbb 100644
--- a/sites/all/modules/memcache/dmemcache.inc
+++ b/sites/all/modules/memcache/dmemcache.inc
@@ -37,7 +37,7 @@ function dmemcache_set($key, $value, $exp = 0, $bin = 'cache', $mc = NULL) {
   $full_key = dmemcache_key($key, $bin);
   $_memcache_statistics[] = array('set', $bin, $full_key, '');
   if ($mc || ($mc = dmemcache_object($bin))) {
-    if (class_exists('Memcached')) {
+    if ($mc instanceof Memcached) {
       return $mc->set($full_key, $value, $exp);
     }
     else {
@@ -73,7 +73,7 @@ function dmemcache_add($key, $value, $exp = 0, $bin = 'cache', $mc = NULL, $flag
   $full_key = dmemcache_key($key, $bin);
   $_memcache_statistics[] = array('add', $bin, $full_key, '');
   if ($mc || ($mc = dmemcache_object($bin))) {
-    if (class_exists('Memcached')) {
+    if ($mc instanceof Memcached) {
       return $mc->add($full_key, $value, $exp);
     }
     else {
@@ -93,17 +93,25 @@ function dmemcache_add($key, $value, $exp = 0, $bin = 'cache', $mc = NULL, $flag
  */
 function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
   global $_memcache_statistics;
+  $result = FALSE;
   $full_key = dmemcache_key($key, $bin);
   $statistics = array('get', $bin, $full_key);
   $success = '0';
-  if ($mc || ($mc = dmemcache_object($bin))) {
-    $result = $mc->get($full_key);
-    if ($result) {
-      $success = '1';
+  if ($mc || $mc = dmemcache_object($bin)) {
+    $track_errors = ini_set('track_errors', '1');
+    $php_errormsg = '';
+
+    $result = @$mc->get($full_key);
+    $statistics[] = (bool) $result;
+    $_memcache_statistics[] = $statistics;
+
+    if (!empty($php_errormsg)) {
+      register_shutdown_function('watchdog', 'memcache', 'Exception caught in dmemcache_get: !msg', array('!msg' => $php_errormsg), WATCHDOG_WARNING);
+      $php_errormsg = '';
     }
+    ini_set('track_errors', $track_errors);
   }
-  $statistics[] = $success;
-  $_memcache_statistics[] = $statistics;
+
   return $result;
 }
 
@@ -122,22 +130,43 @@ function dmemcache_get_multi($keys, $bin = 'cache', $mc = NULL) {
   foreach ($keys as $key => $cid) {
     $full_key = dmemcache_key($cid, $bin);
     $statistics[$full_key] = array('getMulti', $bin, $full_key);
-    $full_keys[] = $full_key;
+    $full_keys[$cid] = $full_key;
   }
   $results = array();
   if ($mc || ($mc = dmemcache_object($bin))) {
-    if (class_exists('Memcached')) {
+    if ($mc instanceof Memcached) {
       $results = $mc->getMulti($full_keys);
     }
-    else {
-      $results = $mc->get($full_keys);
+    elseif ($mc instanceof Memcache) {
+      $track_errors = ini_set('track_errors', '1');
+      $php_errormsg = '';
+
+      $results = @$mc->get($full_keys);
+
+      if (!empty($php_errormsg)) {
+        register_shutdown_function('watchdog', 'memcache', 'Exception caught in dmemcache_get_multi: !msg', array('!msg' => $php_errormsg), WATCHDOG_WARNING);
+        $php_errormsg = '';
+      }
+      ini_set('track_errors', $track_errors);
     }
   }
   foreach ($statistics as $key => $values) {
     $values[] = isset($results[$key]) ? '1': '0';
     $_memcache_statistics[] = $values;
   }
-  return $results;
+
+  // If $results is FALSE, convert it to an empty array.
+  if (!$results) {
+    $results = array();
+  }
+
+  // Convert the full keys back to the cid.
+  $cid_results = array();
+  $cid_lookup = array_flip($full_keys);
+  foreach ($results as $key => $value) {
+    $cid_results[$cid_lookup[$key]] = $value;
+  }
+  return $cid_results;
 }
 
 /**
@@ -153,7 +182,7 @@ function dmemcache_delete($key, $bin = 'cache', $mc = NULL) {
   $full_key = dmemcache_key($key, $bin);
   $_memcache_statistics[] = array('delete', $bin, $full_key, '');
   if ($mc || ($mc = dmemcache_object($bin))) {
-    return $mc->delete($full_key);
+    return $mc->delete($full_key, 0);
   }
   return FALSE;
 }
@@ -176,40 +205,62 @@ function dmemcache_flush($bin = 'cache', $mc = NULL) {
   }
 }
 
-function dmemcache_stats($bin = 'cache', $type = '') {
-  // resolve requests for 'default' type to ''
-  if ($type == 'default') {
-    $type = '';
-  }
-  // resolve requests for 'default' bin to 'cache'.
-  if ($bin == 'default') {
-    $bin = 'cache';
-  }
-  if ($mc = dmemcache_object($bin)) {
-    if (class_exists('Memcached')) {
-      return $mc->getStats();
-    }
-    // The PHP Memcache extension 3.x version throws an error if the stats
-    // type is NULL or not in {reset, malloc, slabs, cachedump, items, sizes}.
-    // If $type is 'default', then no parameter should be passed to the
-    // Memcache memcache_get_extended_stats() function.
-    if ($type == 'default' || $type == '') {
-      if (class_exists('Memcached')) {
-        return $mc->getStats();
-      }
-      else if (class_exists('Memcache')) {
-        return $mc->getExtendedStats();
+function dmemcache_stats($stats_bin = 'cache', $stats_type = 'default', $aggregate = FALSE) {
+  $memcache_bins = variable_get('memcache_bins', array('cache' => 'default'));
+  // The stats_type can be over-loaded with an integer slab id, if doing a
+  // cachedump.  We know we're doing a cachedump if $slab is non-zero.
+  $slab = (int)$stats_type;
+
+  foreach ($memcache_bins as $bin => $target) {
+    if ($stats_bin == $bin) {
+      if ($mc = dmemcache_object($bin)) {
+        if ($mc instanceof Memcached) {
+          $stats[$bin] = $mc->getStats();
+        }
+        // The PHP Memcache extension 3.x version throws an error if the stats
+        // type is NULL or not in {reset, malloc, slabs, cachedump, items,
+        // sizes}. If $stats_type is 'default', then no parameter should be
+        // passed to the Memcache memcache_get_extended_stats() function.
+        else if ($mc instanceof Memcache) {
+          if ($stats_type == 'default' || $stats_type == '') {
+            $stats[$bin] = $mc->getExtendedStats();
+          }
+          // If $slab isn't zero, then we are dumping the contents of a
+          // specific cache slab.
+          else if (!empty($slab))  {
+            $stats[$bin] = $mc->getStats('cachedump', $slab);
+          }
+          else {
+            $stats[$bin] = $mc->getExtendedStats($stats_type);
+          }
+        }
       }
     }
-    else {
-      if (class_exists('Memcached')) {
-        return $mc->getStats();
-      }
-      else if (class_exists('Memcache')) {
-        return $mc->getExtendedStats($type);
+  }
+  // Optionally calculate a sum-total for all servers in the current bin.
+  if ($aggregate) {
+    // Some variables don't logically aggregate.
+    $no_aggregate = array('pid', 'time', 'version', 'pointer_size', 'accepting_conns', 'listen_disabled_num');
+    foreach($stats as $bin => $servers) {
+      if (is_array($servers)) {
+        foreach ($servers as $server) {
+          if (is_array($server)) {
+            foreach ($server as $key => $value) {
+              if (!in_array($key, $no_aggregate)) {
+                if (isset($stats[$bin]['total'][$key])) {
+                  $stats[$bin]['total'][$key] += $value;
+                }
+                else {
+                  $stats[$bin]['total'][$key] = $value;
+                }
+              }
+            }
+          }
+        }
       }
     }
   }
+  return $stats;
 }
 
 /**
@@ -226,7 +277,33 @@ function dmemcache_stats($bin = 'cache', $type = '') {
  * @return an Memcache object or FALSE.
  */
 function dmemcache_object($bin = NULL, $flush = FALSE) {
-  static $memcacheCache = array(), $memcache_servers, $memcache_bins;
+  static $extension, $memcacheCache = array(), $memcache_servers, $memcache_bins, $memcache_persistent, $failed_connection_cache;
+
+  if (!isset($extension)) {
+    // If an extension is specified in settings.php, use that when available.
+    $preferred = variable_get('memcache_extension', NULL);
+    if (isset($preferred) && class_exists($preferred)) {
+      $extension = $preferred;
+    }
+    // If no extension is set, default to Memcache.
+    // The Memcached extension has some features that the older extension lacks
+    // but also an unfixed bug that affects cache clears.
+    // @see http://pecl.php.net/bugs/bug.php?id=16829
+    elseif (class_exists('Memcache')) {
+      $extension = 'Memcache';
+    }
+    elseif (class_exists('Memcached')) {
+      $extension = 'Memcached';
+    }
+
+    // Indicate whether to connect to memcache using a persistent connection.
+    // Note: this only affects the Memcache PECL extension, and does not
+    // affect the Memcached PECL extension.  For a detailed explanation see:
+    //  http://drupal.org/node/822316#comment-4427676
+    if (!isset($memcache_persistent)) {
+      $memcache_persistent = variable_get('memcache_persistent', FALSE);
+    }
+  }
 
   if ($flush) {
     foreach ($memcacheCache as $cluster) {
@@ -257,7 +334,7 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
     }
     else {
       // Create a new Memcache object. Each cluster gets its own Memcache object.
-      if (class_exists('Memcached')) {
+      if ($extension == 'Memcached') {
         $memcache = new Memcached;
         $default_opts = array(
           Memcached::OPT_COMPRESSION => FALSE,
@@ -266,12 +343,14 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
         foreach ($default_opts as $key => $value) {
           $memcache->setOption($key, $value);
         }
+        // See README.txt for setting custom Memcache options when using the
+        // memcached PECL extension.
         $memconf = variable_get('memcache_options', array());
         foreach ($memconf as $key => $value) {
           $memcache->setOption($key, $value);
         }
       }
-      else if (class_exists('Memcache')) {
+      elseif ($extension == 'Memcache') {
         $memcache = new Memcache;
       }
       else {
@@ -283,21 +362,59 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
 
       // Link all the servers to this cluster.
       foreach ($memcache_servers as $s => $c) {
-        if ($c == $cluster) {
+        if ($c == $cluster && !isset($failed_connection_cache[$s])) {
           list($host, $port) = explode(':', $s);
 
-          // This is a server that belongs to this cluster.
-          if (!class_exists('Memcached') && !$init) {
-            // If using PECL memcache extension, use ->connect for first server
-            if ($memcache->connect($host, $port)) {
-              $init = TRUE;
-            }
+          // Support unix sockets in the format 'unix:///path/to/socket'.
+          if ($host == 'unix') {
+            // When using unix sockets use the full path for $host.
+            $host = $s;
+            // Port is always 0 for unix sockets.
+            $port = 0;
           }
-          else {
-            if ($memcache->addServer($host, $port) && !$init) {
-              $init = TRUE;
+
+          // Using the Memcache PECL extension.
+          if ($memcache instanceof Memcache) {
+            // When using the PECL memcache extension, we must use ->(p)connect
+            // for the first connection.
+            if (!$init) {
+              $track_errors = ini_set('track_errors', '1');
+              $php_errormsg = '';
+
+              if ($memcache_persistent && @$memcache->pconnect($host, $port)) {
+                $init = TRUE;
+              }
+              elseif (!$memcache_persistent && @$memcache->connect($host, $port)) {
+                $init = TRUE;
+              }
+
+              if (!empty($php_errormsg)) {
+                register_shutdown_function('watchdog', 'memcache', 'Exception caught in dmemcache_object: !msg', array('!msg' => $php_errormsg), WATCHDOG_WARNING);
+                $php_errormsg = '';
+              }
+              ini_set('track_errors', $track_errors);
+            }
+            else {
+              $memcache->addServer($host, $port, $memcache_persistent);
             }
           }
+          else if ($memcache->addServer($host, $port) && !$init) {
+            $init = TRUE;
+          }
+
+          if (!$init) {
+            // Ensure we use an available t() function.
+            $t = get_t();
+            $error_msg = $t(
+              'Failed to connect to memcache server: %server',
+              array('%server' => $s)
+            );
+            // We can't use watchdog because this happens in a bootstrap phase
+            // where watchdog is non-functional. Thus use trigger_error() to
+            // start drupal_error_handler().
+            trigger_error($error_msg, E_USER_ERROR);
+            $failed_connection_cache[$s] = FALSE;
+          }
         }
       }
 
@@ -321,18 +438,14 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
 }
 
 function dmemcache_key($key, $bin = 'cache') {
-  static $prefix;
-  // memcache_key_prefix can be set in settings.php to support site namespaces
-  // in a multisite environment.
-  if (empty($prefix)) {
-    if ($prefix = variable_get('memcache_key_prefix', '')) {
-      $prefix .= '-';
-    }
-    // When simpletest is running, emulate the simpletest database prefix here
-    // to avoid the child site setting cache entries in the parent site.
-    if (isset($GLOBALS['drupal_test_info']['test_run_id'])) {
-      $prefix .= $GLOBALS['drupal_test_info']['test_run_id'];
-    }
+  $prefix = '';
+  if ($prefix = variable_get('memcache_key_prefix', '')) {
+    $prefix .= '-';
+  }
+  // When simpletest is running, emulate the simpletest database prefix here
+  // to avoid the child site setting cache entries in the parent site.
+  if (isset($GLOBALS['drupal_test_info']['test_run_id'])) {
+    $prefix .= $GLOBALS['drupal_test_info']['test_run_id'];
   }
   $full_key = urlencode($prefix . $bin . '-' . $key);
 
diff --git a/sites/all/modules/memcache/memcache-lock-code.inc b/sites/all/modules/memcache/memcache-lock-code.inc
new file mode 100644
index 0000000000000000000000000000000000000000..374d02b0374bb04a27f88b260454d13a845dcb40
--- /dev/null
+++ b/sites/all/modules/memcache/memcache-lock-code.inc
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @file
+ * A memcache based implementation of a locking mechanism.
+ * See includes/lock.inc for documenation
+ *
+ * ATTENTION: Don't include this file directly - use the memcache-lock.inc to
+ * have a failover solution.
+ */
+
+/**
+ * Initialize the locking system.
+ */
+function lock_initialize() {
+  global $locks;
+
+  $locks = array();
+}
+
+/**
+ * Acquire (or renew) a lock, but do not block if it fails.
+ *
+ * @param $name
+ *   The name of the lock.
+ * @param $timeout
+ *   A number of seconds (int) before the lock expires (minimum of 1).
+ * @return
+ *   TRUE if the lock was acquired, FALSE if it failed.
+ */
+function lock_acquire($name, $timeout = 30) {
+  global $locks;
+
+  // Ensure that the timeout is at least 1 sec. This is a limitation
+  // imposed by memcached.
+  $timeout = (int) max($timeout, 1);
+
+  if (dmemcache_add($name, _lock_id(), $timeout, 'semaphore')) {
+    $locks[$name] = _lock_id();
+  }
+  elseif ($result = dmemcache_get($name, 'semaphore') && isset($locks[$name]) && $locks[$name] == _lock_id()) {
+    // Only renew the lock if we already set it and it has not expired.
+    dmemcache_set($name, _lock_id(), $timeout, 'semaphore');
+  }
+  else {
+    // Failed to acquire the lock.  Unset the key from the $locks array even if
+    // not set, PHP 5+ allows this without error or warning.
+    unset($locks[$name]);
+  }
+
+  return isset($locks[$name]);
+}
+
+/**
+ * Check if lock acquired by a different process may be available.
+ *
+ * If an existing lock has expired, it is removed.
+ *
+ * @param $name
+ *   The name of the lock.
+ * @return
+ *   TRUE if there is no lock or it was removed, FALSE otherwise.
+ */
+function lock_may_be_available($name) {
+  return !dmemcache_get($name, 'semaphore');
+}
+
+/**
+ * Wait for a lock to be available.
+ *
+ * This function may be called in a request that fails to acquire a desired
+ * lock. This will block further execution until the lock is available or the
+ * specified delay in seconds is reached. This should not be used with locks
+ * that are acquired very frequently, since the lock is likely to be acquired
+ * again by a different request while waiting.
+ *
+ * @param $name
+ *   The name of the lock.
+ * @param $delay
+ *   The maximum number of seconds to wait, as an integer.
+ * @return
+ *   TRUE if the lock holds, FALSE if it is available.
+ */
+function lock_wait($name, $delay = 30) {
+  // Pause the process for short periods between calling
+  // lock_may_be_available(). This prevents hitting the database with constant
+  // database queries while waiting, which could lead to performance issues.
+  // However, if the wait period is too long, there is the potential for a
+  // large number of processes to be blocked waiting for a lock, especially
+  // if the item being rebuilt is commonly requested. To address both of these
+  // concerns, begin waiting for 25ms, then add 25ms to the wait period each
+  // time until it reaches 500ms. After this point polling will continue every
+  // 500ms until $delay is reached.
+
+  // $delay is passed in seconds, but we will be using usleep(), which takes
+  // microseconds as a parameter. Multiply it by 1 million so that all
+  // further numbers are equivalent.
+  $delay = (int) $delay * 1000000;
+
+  // Begin sleeping at 25ms.
+  $sleep = 25000;
+  while ($delay > 0) {
+    // This function should only be called by a request that failed to get a
+    // lock, so we sleep first to give the parallel request a chance to finish
+    // and release the lock.
+    usleep($sleep);
+    // After each sleep, increase the value of $sleep until it reaches
+    // 500ms, to reduce the potential for a lock stampede.
+    $delay = $delay - $sleep;
+    $sleep = min(500000, $sleep + 25000, $delay);
+    if (!dmemcache_get($name, 'semaphore')) {
+      // No longer need to wait.
+      return FALSE;
+    }
+  }
+  // The caller must still wait longer to get the lock.
+  return TRUE;
+}
+
+/**
+ * Release a lock previously acquired by lock_acquire().
+ *
+ * This will release the named lock if it is still held by the current request.
+ *
+ * @param $name
+ *   The name of the lock.
+ */
+function lock_release($name) {
+  global $locks;
+
+  dmemcache_delete($name, 'semaphore');
+  // We unset unconditionally since caller assumes lock is released anyway.
+  unset($locks[$name]);
+}
+
+/**
+ * Generate a unique identifier for locks generated during this request.
+ */
+function _lock_id() {
+  static $lock_id;
+  if (!isset($lock_id)) {
+    $lock_id = uniqid(mt_rand(), TRUE);
+    // We only register a shutdown function if a lock is used.
+    register_shutdown_function('lock_release_all', $lock_id);
+  }
+  return $lock_id;
+}
+
+/**
+ * Release all locks acquired by this request.
+ */
+function lock_release_all($lock_id) {
+  global $locks;
+  foreach ($locks as $name => $id) {
+    $value = dmemcache_get($name, 'semaphore');
+
+    if ($value == $id) {
+      dmemcache_delete($name, 'semaphore');
+    }
+  }
+}
diff --git a/sites/all/modules/memcache/memcache-lock.inc b/sites/all/modules/memcache/memcache-lock.inc
index 5d252840668f0240c92e82d558d39af0257475a5..bc0f936843c995c4e971b3c21ec1d05757c7c31a 100644
--- a/sites/all/modules/memcache/memcache-lock.inc
+++ b/sites/all/modules/memcache/memcache-lock.inc
@@ -6,117 +6,13 @@
  * See includes/lock.inc for documenation
  */
 
-// Set up a define to make the code more readable, so we know we're setting a
-// value for our lock and not simply passing dmemcache a flag.
-define('LOCK_VALUE', TRUE);
-
-
-/**
- * Initialize the locking system.
- */
-function lock_initialize() {
-  global $locks;
-
-  $locks = array();
-}
-
-/**
- * Acquire (or renew) a lock, but do not block if it fails.
- *
- * @param $name
- *   The name of the lock.
- * @param $timeout
- *   A number of seconds (int) before the lock expires (minimum of 1).
- * @return
- *   TRUE if the lock was acquired, FALSE if it failed.
- */
-function lock_acquire($name, $timeout = 30) {
-  global $locks;
-
-  // Ensure that the timeout is at least 1 sec. This is a limitation
-  // imposed by memcached.
-  $timeout = max($timeout, 1);
-  $now = microtime(TRUE);
-  $expire = $now + $timeout;
-
-  $result = dmemcache_get($name, 'semaphore');
-  if ($result && isset($locks[$name]) && $locks[$name] > $now) {
-    // Only renew the lock if we already set it and it has not expired.
-    if (dmemcache_set($name, LOCK_VALUE, $timeout, 'semaphore')) {
-      $locks[$name] = $expire;
-    }
-  }
-  else if (dmemcache_add($name, LOCK_VALUE, $timeout, 'semaphore')) {
-    $locks[$name] = $expire;
-  }
-  else {
-    // Failed to acquire the lock.  Unset the key from the $locks array even if
-    // not set, PHP 5+ allows this without error or warning.
-    unset($locks[$name]);
-  }
-
-  return isset($locks[$name]);
-}
-
-/**
- * Check if lock acquired by a different process may be available.
- *
- * If an existing lock has expired, it is removed.
- *
- * @param $name
- *   The name of the lock.
- * @return
- *   TRUE if there is no lock or it was removed, FALSE otherwise.
- */
-function lock_may_be_available($name) {
-  return !dmemcache_get($name, 'semaphore');
-}
-
-/**
- * Wait for a lock to be available.
- *
- * This function may be called in a request that fails to acquire a desired
- * lock. This will block further execution until the lock is available or the
- * specified delay in seconds is reached. This should not be used with locks
- * that are acquired very frequently, since the lock is likely to be acquired
- * again by a different request during the sleep().
- *
- * @param $name
- *   The name of the lock.
- * @param $delay
- *   The maximum number of seconds to wait, as an integer.
- * @return
- *   TRUE if the lock holds, FALSE if it is available.
- */
-function lock_wait($name, $delay = 30) {
-  $delay = (int) $delay;
-  while ($delay--) {
-    // This function should only be called by a request that failed to get a
-    // lock, so we sleep first to give the parallel request a chance to finish
-    // and release the lock.
-    sleep(1);
-    if (!dmemcache_get($name, 'semaphore')) {
-      // No longer need to wait.
-      return FALSE;
-    }
-  }
-  // The caller must still wait longer to get the lock.
-  return TRUE;
+require_once dirname(__FILE__) . '/dmemcache.inc';
+
+// Check if memcached is available - if not include default lock handler.
+// @todo get rid of this conditional include as soon as this is done:
+// http://drupal.org/node/1225404
+$lock_file = dirname(__FILE__) . '/memcache-lock-code.inc';
+if (!dmemcache_object('semaphore')) {
+  $lock_file = DRUPAL_ROOT . '/includes/lock.inc';
 }
-
-/**
- * Release a lock previously acquired by lock_acquire().
- *
- * This will release the named lock if it is still held by the current request.
- *
- * @param $name
- *   The name of the lock.
- */
-function lock_release($name) {
-  global $locks;
-
-  dmemcache_delete($name, 'semaphore');
-  // We unset unconditionally since caller assumes lock is released anyway.
-  unset($locks[$name]);
-}
-
+require_once $lock_file;
\ No newline at end of file
diff --git a/sites/all/modules/memcache/memcache.inc b/sites/all/modules/memcache/memcache.inc
index 7718880d2c6499ad2be9ce1ccba3dbea73a26bfe..7d438eabcc7bc339d5b51b439246fffe8c55049e 100644
--- a/sites/all/modules/memcache/memcache.inc
+++ b/sites/all/modules/memcache/memcache.inc
@@ -1,12 +1,14 @@
 <?php
 
-require_once 'dmemcache.inc';
+require_once dirname(__FILE__) . '/dmemcache.inc';
 
 /**
  * Defines the period after which wildcard clears are not considered valid.
  */
 define('MEMCACHE_WILDCARD_INVALIDATE', 86400 * 28);
 
+define('MEMCACHE_CONTENT_CLEAR', 'MEMCACHE_CONTENT_CLEAR');
+
 /** Implementation of cache.inc with memcache logic included **/
 
 class MemCacheDrupal implements DrupalCacheInterface {
@@ -14,12 +16,9 @@ class MemCacheDrupal implements DrupalCacheInterface {
     $this->memcache = dmemcache_object($bin);
     $this->bin = $bin;
 
-    $this->wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
-    $this->invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
-    $this->cache_lifetime = variable_get('cache_lifetime', 0);
-    $this->cache_flush = variable_get('cache_flush_' . $this->bin);
-    $this->flushed = min($this->cache_flush, REQUEST_TIME - $this->cache_lifetime);
+    $this->reloadVariables();
   }
+
   function get($cid) {
     $cache = dmemcache_get($cid, $this->bin, $this->memcache);
     return $this->valid($cid, $cache) ? $cache : FALSE;
@@ -32,73 +31,89 @@ class MemCacheDrupal implements DrupalCacheInterface {
         // This object has expired, so don't return it.
         unset($results[$cid]);
       }
-      else {
-        // Remove items from the referenced $cids array that we are returning,
-        // per the comment in cache_get_multiple() in includes/cache.inc.
-        unset($cids[$result->cid]);
-      }
     }
+    // Remove items from the referenced $cids array that we are returning,
+    // per the comment in cache_get_multiple() in includes/cache.inc.
+    $cids = array_diff($cids, array_keys($results));
     return $results;
   }
 
   protected function valid($cid, $cache) {
-    if (!isset($cache) || !is_object($cache)) {
-      return FALSE;
-    }
-
-    // The wildcard_valid() function has overhead due to a call to
-    // dmemcache_get_multi() to fetch possible wildcard flushes. Since some
-    // bins never have wildcard clears with a cid, we can shortcut these checks.
-    if (!empty($this->wildcard_flushes[$this->bin]) &&
-        max($this->wildcard_flushes[$this->bin]) >= (REQUEST_TIME - $this->invalidate) &&
-        !$this->wildcard_valid($cid, $cache))  {
-      return FALSE;
-    }
-
-    // Determine when the current bin was last flushed.
-    $item_flushed_globally = $cache->created && $this->cache_flush &&
-                             $this->cache_lifetime &&
-                             ($cache->created <= $this->flushed);
-
-    // Look in the session to determine if this item was flushed for the
-    // current user (ie, if they posted a comment and are viewing a cached page)
-    $cache_bins = isset($_SESSION['cache_flush']) ? $_SESSION['cache_flush'] : NULL;
-    $item_flushed_for_user = !empty($cache_bins) &&
-                             isset($cache_bins[$this->bin]) &&
-                             ($cache->created < $cache_bins[$this->bin]);
-    if ($item_flushed_for_user) {
-      return FALSE;
+    if ($cache) {
+      $cache_tables = isset($_SESSION['cache_flush']) ? $_SESSION['cache_flush'] : NULL;
+      // Items that have expired are invalid.
+      if (isset($cache->expire) && $cache->expire !== CACHE_PERMANENT && $cache->expire <= $_SERVER['REQUEST_TIME']) {
+        // If the memcache_stampede_protection variable is set, allow one process
+        // to rebuild the cache entry while serving expired content to the
+        // rest. Note that core happily returns expired cache items as valid and
+        // relies on cron to expire them, but this is mostly reliant on its
+        // use of CACHE_TEMPORARY which does not map well to memcache.
+        // @see http://drupal.org/node/534092
+        if (variable_get('memcache_stampede_protection', FALSE)) {
+          // The process that acquires the lock will get a cache miss, all
+          // others will get a cache hit.
+          if (lock_acquire("memcache_$cid:$this->bin", variable_get('memcache_stampede_semaphore', 15))) {
+            $cache = FALSE;
+          }
+        }
+        else {
+          $cache = FALSE;
+        }
+      }
+      // Items created before the last full wildcard flush against this bin are
+      // invalid.
+      elseif ($cache->created <= $this->cache_flush) {
+        $cache = FALSE;
+      }
+      // Items created before the last content flush on this bin i.e.
+      // cache_clear_all() are invalid.
+      elseif ($cache->expire != CACHE_PERMANENT && $cache->created + $this->cache_lifetime <= $this->cache_content_flush) {
+        $cache = FALSE;
+      }
+      // Items cached before the cache was last flushed by the current user are
+      // invalid.
+      elseif ($cache->expire != CACHE_PERMANENT && is_array($cache_tables) && isset($cache_tables[$this->bin]) && $cache_tables[$this->bin] >= $cache->created) {
+        // Cache item expired, return FALSE.
+        $cache = FALSE;
+      }
+      // Finally, check for wildcard clears against this cid.
+      else {
+        if (!$this->wildcard_valid($cid, $cache)) {
+          $cache = FALSE;
+        }
+      }
     }
 
-    // The item can be expired if:
-    // - A liftetime is set and the item is older than both the lifetime and
-    //   the global flush.
-    // - The item has been create before the bin was flushed for this user.
-    // - The item could simply expire.
-    //
-    // For the two global cases we try and grab a lock.  If we get the lock, we
-    // return FALSE instead of the cached object which should cause it to be
-    // rebuilt.  If we do not get the lock, we return the cached object despite
-    // it's expired  The goal here is to avoid cache stampedes.
-    // By default the cache stampede semaphore is held for 15 seconds.  This
-    // can be adjusted by setting the memcache_stampede_semaphore variable.
-    $item_expired = isset($cache->expire) &&
-                    $cache->expire !== CACHE_PERMANENT &&
-                    $cache->expire <= REQUEST_TIME;
-    if ($item_flushed_globally || $item_expired) {
-      // To avoid a stampede, return TRUE despite the item being expired if
-      // a previous process set the stampede semaphore already. However only
-      // do this if the data is less than 30 minutes stale.
-      if ((REQUEST_TIME - $cache->expire) >= variable_get('memcache_max_staleness', 1800) ||
-          dmemcache_add($cid . '_semaphore', '', variable_get('memcache_stampede_semaphore', 15), $this->bin)) {
-        return FALSE;
+    // On cache misses, attempt to avoid stampedes when the
+    // memcache_stampede_protection variable is enabled.
+    if (!$cache) {
+      if (variable_get('memcache_stampede_protection', FALSE) && !lock_acquire("memcache_$cid:$this->bin", variable_get('memcache_stampede_semaphore', 15))) {
+        // Prevent any single request from waiting more than three times due to
+        // stampede protection. By default this is a maximum total wait of 15
+        // seconds. This accounts for two possibilities - a cache and lock miss
+        // more than once for the same item. Or a cache and lock miss for
+        // different items during the same request.
+        // @todo: it would be better to base this on time waited rather than
+        // number of waits, but the lock API does not currently provide this
+        // information. Currently the limit will kick in for three waits of 25ms
+        // or three waits of 5000ms.
+        static $lock_count = 0;
+        $lock_count++;
+        if ($lock_count <= variable_get('memcache_stampede_wait_limit', 3)) {
+          // The memcache_stampede_semaphore variable was used in previous releases
+          // of memcache, but the max_wait variable was not, so by default divide
+          // the semaphore value by 3 (5 seconds).
+          lock_wait("memcache_$cid:$this->bin", variable_get('memcache_stampede_wait_time', 5));
+          $cache = $this->get($cid);
+        }
       }
     }
-    return TRUE;
+
+    return (bool) $cache;
   }
 
   function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL) {
-    $created = REQUEST_TIME;
+    $created = time();
 
     // Create new cache object.
     $cache = new stdClass;
@@ -131,6 +146,36 @@ class MemCacheDrupal implements DrupalCacheInterface {
   }
 
   function clear($cid = NULL, $wildcard = FALSE) {
+    if ($this->memcache === FALSE) {
+      // No memcache connection.
+      return;
+    }
+
+    // It is not possible to detect a cache_clear_all() call other than looking
+    // at the backtrace unless http://drupal.org/node/81461 is added.
+    $backtrace = debug_backtrace();
+    if ($cid == MEMCACHE_CONTENT_CLEAR || (isset($backtrace[2]) && $backtrace[2]['function'] == 'cache_clear_all' && empty($backtrace[2]['args']))) {
+      // Update the timestamp of the last global flushing of this bin.  When
+      // retrieving data from this bin, we will compare the cache creation
+      // time minus the cache_flush time to the cache_lifetime to determine
+      // whether or not the cached item is still valid.
+      $this->cache_content_flush = time();
+      $this->variable_set('cache_content_flush_' . $this->bin, $this->cache_content_flush);
+      if (variable_get('cache_lifetime', 0)) {
+        // We store the time in the current user's session. We then simulate
+        // that the cache was flushed for this user by not returning cached
+        // data to this user that was cached before the timestamp.
+        if (isset($_SESSION['cache_flush']) && is_array($_SESSION['cache_flush'])) {
+          $cache_bins = $_SESSION['cache_flush'];
+        }
+        else {
+          $cache_bins = array();
+        }
+        // Use time() rather than request time here for correctness.
+        $cache_tables[$this->bin] = $this->cache_content_flush;
+        $_SESSION['cache_flush'] = $cache_tables;
+      }
+    }
     if (empty($cid) || $wildcard === TRUE) {
       // system_cron() flushes all cache bins returned by hook_flush_caches()
       // with cache_clear_all(NULL, $bin); This is for garbage collection with
@@ -142,26 +187,29 @@ class MemCacheDrupal implements DrupalCacheInterface {
       elseif ($cid == '*') {
         $cid = '';
       }
-      if ($this->cache_lifetime && empty($cid)) {
+      if (empty($cid)) {
         // Update the timestamp of the last global flushing of this bin.  When
         // retrieving data from this bin, we will compare the cache creation
         // time minus the cache_flush time to the cache_lifetime to determine
         // whether or not the cached item is still valid.
-        variable_set("cache_flush_$this->bin", REQUEST_TIME);
-        $this->flushed = REQUEST_TIME;
+        $this->cache_flush = time();
+        $this->variable_set("cache_flush_$this->bin", $this->cache_flush);
+        $this->flushed = min($this->cache_flush, time() - $this->cache_lifetime);
 
-        // We store the time in the current user's session which is saved into
-        // the sessions table by sess_write().  We then simulate that the cache
-        // was flushed for this user by not returning cached data to this user
-        // that was cached before the timestamp.
-        if (isset($_SESSION['cache_flush']) && is_array($_SESSION['cache_flush'])) {
-          $cache_bins = $_SESSION['cache_flush'];
-        }
-        else {
-          $cache_bins = array();
+        if ($this->cache_lifetime) {
+          // We store the time in the current user's session which is saved into
+          // the sessions table by sess_write().  We then simulate that the cache
+          // was flushed for this user by not returning cached data to this user
+          // that was cached before the timestamp.
+          if (isset($_SESSION['cache_flush']) && is_array($_SESSION['cache_flush'])) {
+            $cache_bins = $_SESSION['cache_flush'];
+          }
+          else {
+            $cache_bins = array();
+          }
+          $cache_bins[$this->bin] = $this->cache_flush;
+          $_SESSION['cache_flush'] = $cache_bins;
         }
-        $cache_bins[$this->bin] = REQUEST_TIME;
-        $_SESSION['cache_flush'] = $cache_bins;
       }
       else {
         // Register a wildcard flush for current cid
@@ -181,7 +229,7 @@ class MemCacheDrupal implements DrupalCacheInterface {
    * value against this single-value sum tells us whether or not a new wildcard
    * flush has affected the cached item.
    */
-  private function wildcard_flushes($cid) {
+  protected function wildcard_flushes($cid) {
     return array_sum($this->wildcards($cid));
   }
 
@@ -190,12 +238,21 @@ class MemCacheDrupal implements DrupalCacheInterface {
    * statically so multiple cache requests for the same item on the same page
    * load doesn't add overhead.
    */
-  private function wildcards($cid, $flush = FALSE) {
+  protected function wildcards($cid, $flush = FALSE) {
     static $wildcards = array();
     $matching = array();
 
-    if (isset($this->wildcard_flushes[$this->bin]) &&
-      is_array($this->wildcard_flushes[$this->bin])) {
+    $length = strlen($cid);
+
+    if (isset($this->wildcard_flushes[$this->bin]) && is_array($this->wildcard_flushes[$this->bin])) {
+      // Wildcard flushes per table are keyed by a substring equal to the
+      // shortest wildcard clear on the table so far. So if the shortest
+      // wildcard was "links:foo:", and the cid we're checking for is
+      // "links:bar:bar", then the key will be "links:bar:".
+      $keys = array_keys($this->wildcard_flushes[$this->bin]);
+      $wildcard_length = strlen(reset($keys));
+      $wildcard_key = substr($cid, 0, $wildcard_length);
+
       // Determine which lookups we need to perform to determine whether or not
       // our cid was impacted by a wildcard flush.
       $lookup = array();
@@ -203,15 +260,16 @@ class MemCacheDrupal implements DrupalCacheInterface {
       // Find statically cached wildcards, and determine possibly matching
       // wildcards for this cid based on a history of the lengths of past
       // valid wildcard flushes in this bin.
-      foreach ($this->wildcard_flushes[$this->bin] as $length => $timestamp) {
-        if ($timestamp >= (REQUEST_TIME - $this->invalidate)) {
-          $key = '.wildcard-' . substr($cid, 0, $length);
-          $wildcard = dmemcache_key($key, $this->bin);
-          if (isset($wildcards[$this->bin][$wildcard])) {
-            $matching[$wildcard] = $wildcards[$this->bin][$wildcard];
-          }
-          else {
-            $lookup[$wildcard] = $key;
+      if (isset($this->wildcard_flushes[$this->bin][$wildcard_key])) {
+        foreach ($this->wildcard_flushes[$this->bin][$wildcard_key] as $flush_length => $timestamp) {
+          if ($length >= $flush_length && $timestamp >= ($_SERVER['REQUEST_TIME'] - $this->invalidate)) {
+            $wildcard = '.wildcard-' . substr($cid, 0, $flush_length);
+            if (isset($wildcards[$this->bin][$wildcard])) {
+              $matching[$wildcard] = $wildcards[$this->bin][$wildcard];
+            }
+            else {
+              $lookup[$wildcard] = $wildcard;
+            }
           }
         }
       }
@@ -234,31 +292,55 @@ class MemCacheDrupal implements DrupalCacheInterface {
 
         // Also store failed lookups in our static cache, so we don't have to
         // do repeat lookups on single page loads.
-        foreach ($lookup as $wildcard => $key) {
-          $wildcards[$this->bin][$wildcard] = 0;
+        foreach ($lookup as $key => $key) {
+          $wildcards[$this->bin][$key] = 0;
         }
       }
     }
+
     if ($flush) {
+      $key_length = $length;
+      if (isset($this->wildcard_flushes[$this->bin])) {
+        $keys = array_keys($this->wildcard_flushes[$this->bin]);
+        $key_length = strlen(reset($keys));
+      }
+      $key = substr($cid, 0, $key_length);
       // Avoid too many calls to variable_set() by only recording a flush for
       // a fraction of the wildcard invalidation variable, per cid length.
       // Defaults to 28 / 4, or one week.
-      $length = strlen($cid);
-      if (!isset($this->wildcard_flushes[$this->bin][$length]) ||
-          ($_SERVER['REQUEST_TIME'] - $this->wildcard_flushes[$this->bin][$length] > $this->invalidate / 4)) {
-        $this->wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
-        $this->wildcard_flushes[$this->bin][$length] = $_SERVER['REQUEST_TIME'];
+      if (!isset($this->wildcard_flushes[$this->bin][$key][$length]) || ($_SERVER['REQUEST_TIME'] - $this->wildcard_flushes[$this->bin][$key][$length] > $this->invalidate / 4)) {
+
+        // If there are more than 50 different wildcard keys for this bin
+        // shorten the key by one, this should reduce variability by
+        // an order of magnitude and ensure we don't use too much memory.
+        if (isset($this->wildcard_flushes[$this->bin]) && count($this->wildcard_flushes[$this->bin]) > 50) {
+          $key = substr($cid, 0, $key_length - 1);
+          $length = strlen($key);
+        }
+
+        // If this is the shortest key length so far, we need to remove all
+        // other wildcards lengths recorded so far for this bin and start
+        // again. This is equivalent to a full cache flush for this table, but
+        // it ensures the minimum possible number of wildcards are requested
+        // along with cache consistency.
+        if ($length < $key_length) {
+          $this->wildcard_flushes[$this->bin] = array();
+          $this->variable_set("cache_flush_$this->bin", time());
+          $this->cache_flush = time();
+        }
+        $key = substr($cid, 0, $key_length);
+        $this->wildcard_flushes[$this->bin][$key][$length] = $_SERVER['REQUEST_TIME'];
+
         variable_set('memcache_wildcard_flushes', $this->wildcard_flushes);
       }
-      $wildcard = dmemcache_key('.wildcard-' . $cid, $this->bin);
-      if (isset($wildcards[$this->bin][$wildcard]) && $wildcards[$this->bin][$wildcard] != 0) {
-        $this->memcache->increment($wildcard);
-        $wildcards[$this->bin][$wildcard]++;
+      $key = '.wildcard-' . $cid;
+      if (isset($wildcards[$this->bin][$key])) {
+        $wildcards[$this->bin][$key]++;
       }
       else {
-        $wildcards[$this->bin][$wildcard] = 1;
-        dmemcache_set('.wildcard-' . $cid, '1', 0, $this->bin);
+        $wildcards[$this->bin][$key] = 1;
       }
+      dmemcache_set($key, $wildcards[$this->bin][$key], 0, $this->bin);
     }
     return $matching;
   }
@@ -266,7 +348,7 @@ class MemCacheDrupal implements DrupalCacheInterface {
   /**
    * Check if a wildcard flush has invalidated the current cached copy.
    */
-  private function wildcard_valid($cid, $cache) {
+  protected function wildcard_valid($cid, $cache) {
     // Previously cached content won't have ->flushes defined.  We could
     // force flush, but instead leave this up to the site admin.
     $flushes = isset($cache->flushes) ? (int)$cache->flushes : 0;
@@ -280,5 +362,40 @@ class MemCacheDrupal implements DrupalCacheInterface {
     // We do not know so err on the safe side?
     return FALSE;
   }
-}
 
+  /**
+   * Helper function to reload variables.
+   *
+   * This is used by the tests to verify that the cache object used the correct
+   * settings.
+   */
+  function reloadVariables() {
+    $this->wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
+    $this->invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
+    $this->cache_lifetime = variable_get('cache_lifetime', 0);
+    $this->cache_flush = variable_get('cache_flush_' . $this->bin);
+    $this->cache_content_flush = variable_get('cache_content_flush_' . $this->bin, 0);
+    $this->flushed = min($this->cache_flush, REQUEST_TIME - $this->cache_lifetime);
+  }
+
+  /**
+   * Re-implementation of variable_set() that writes through instead of clearing.
+   */
+  function variable_set($name, $value) {
+    global $conf;
+
+    db_merge('variable')
+      ->key(array('name' => $name))
+      ->fields(array('value' => serialize($value)))
+      ->execute();
+    // If the variables are cached, get a fresh copy, update with the new value
+    // and set it again.
+    if ($cached = cache_get('variables', 'cache_bootstrap')) {
+      $variables = $cached->data;
+      $variables[$name] = $value;
+      cache_set('variables', $variables, 'cache_bootstrap');
+    }
+    // If the variables aren't cached, there's no need to do anything.
+    $conf[$name] = $value;
+  }
+}
diff --git a/sites/all/modules/memcache/memcache.info b/sites/all/modules/memcache/memcache.info
index b04fbcbe95aa3ee265fa14f2cd02650dbd29a802..4831c2a6f822519cb602dd2f202ddbc0e74622bc 100644
--- a/sites/all/modules/memcache/memcache.info
+++ b/sites/all/modules/memcache/memcache.info
@@ -1,14 +1,14 @@
 name = Memcache
 description = High performance integration with memcache.
-package = Caching
+package = Performance and scalability
 core = 7.x
-files[] = memcache.inc
-files[] = memcache.module
-files[] = memcache.test
+files[] = tests/memcache.test
+files[] = tests/memcache-session.test
+files[] = tests/memcache-lock.test
 
-; Information added by drupal.org packaging script on 2011-05-27
-version = "7.x-1.0-beta4"
+; Information added by drupal.org packaging script on 2012-01-07
+version = "7.x-1.0-rc3"
 core = "7.x"
 project = "memcache"
-datestamp = "1306500116"
+datestamp = "1325917859"
 
diff --git a/sites/all/modules/memcache/memcache.install b/sites/all/modules/memcache/memcache.install
new file mode 100644
index 0000000000000000000000000000000000000000..347c3396677b42b6a439cb8102ad7a8f386eb622
--- /dev/null
+++ b/sites/all/modules/memcache/memcache.install
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Implements hook_requirements().
+ */
+function memcache_requirements($phase) {
+  $requirements = array();
+  $t = get_t();
+  $memcache = extension_loaded('memcache');
+  $memcached = extension_loaded('memcached');
+  if ($phase == 'install' || $phase == 'runtime') {
+    if (!$memcache && !$memcached) {
+      $requirements['memcache_extension']['severity'] = REQUIREMENT_ERROR;
+      $requirements['memcache_extension']['title'] = $t('Extensions not available');
+      $requirements['memcache_extension']['value'] = $t('Either the <a href="http://php.net/manual/en/book.memcache.php">memcache</a> or <a href="http://php.net/manual/en/book.memcached.php">memcached</a> extensions must be installed in order to use memcache integration.');
+    }
+  }
+  if ($phase == 'runtime') {
+    if ($memcache) {
+      // @todo: consider adding minimum version requirement for extensions.
+      $requirements['memcache_extension_version']['severity'] = REQUIREMENT_OK;
+      $requirements['memcache_extension_version']['title'] = $t('Memcache version');
+      $requirements['memcache_extension_version']['value'] = phpversion('memcache');
+    }
+    if ($memcached) {
+      $requirements['memcached_extension_version']['severity'] = REQUIREMENT_OK;
+      $requirements['memcached_extension_version']['title'] = $t('Memcached version');
+      $requirements['memcached_extension_version']['value'] = phpversion('memcached');
+    }
+    // Confirm that dmemcache.inc has been included.
+    $requirements['memcache_inc']['title'] = $t('Memcache integration');
+    if (function_exists('dmemcache_set')) {
+      $requirements['memcache_inc']['severity'] = REQUIREMENT_OK;
+      $requirements['memcache_inc']['title'] = $t('Memcache integration');
+      $requirements['memcache_inc']['value'] = $t('Memcache integration functions are loaded');
+    }
+    else {
+      $requirements['memcache_inc']['severity'] = REQUIREMENT_WARNING;
+      $requirements['memcache_inc']['title'] = $t('Memcache integration');
+      $requirements['memcache_inc']['value'] = $t('Memcache integration is not currently loaded.');
+      $requirements['memcache_inc']['description'] = $t('Check README.txt and ensure that memcache.inc is configured correctly in settings.php');
+    }
+  }
+  return $requirements;
+}
+
+/**
+ * Remove the memcache_widlcard_flushes variable since its structure has changed.
+ */
+function memcache_update_7000() {
+  variable_del('memcache_wildcard_flushes');
+}
diff --git a/sites/all/modules/memcache/memcache.module b/sites/all/modules/memcache/memcache.module
new file mode 100644
index 0000000000000000000000000000000000000000..92f76a3195e3c0f01ca21eb1053695939c861c57
--- /dev/null
+++ b/sites/all/modules/memcache/memcache.module
@@ -0,0 +1,6 @@
+<?php
+/**
+ * Provides very limited functionality such as hook_requirements().
+ * memcache.inc must be configured in settings.php, and memcache.module is not
+ * necessary to use memcache as a cache backend.
+ */
diff --git a/sites/all/modules/memcache/memcache.test b/sites/all/modules/memcache/memcache.test
deleted file mode 100644
index 89abd6b7f25a6f6472210be91512227af831fd3e..0000000000000000000000000000000000000000
--- a/sites/all/modules/memcache/memcache.test
+++ /dev/null
@@ -1,541 +0,0 @@
-<?php
-
-class MemcacheTestCase extends DrupalWebTestCase {
-  protected $default_bin = 'cache';
-  protected $default_cid = 'test_temporary';
-  protected $default_value = 'MemcacheTest';
-
-  function setUp() {
-    include_once drupal_get_path('module', 'memcache') . '/memcache.inc';
-    variable_set('cache_default_class', 'MemCacheDrupal');
-    parent::setUp();
-  }
-
-  /**
-   * Check whether or not a cache entry exists.
-   *
-   * @param $cid
-   *   The cache id.
-   * @param $var
-   *   The variable the cache should contain.
-   * @param $bin
-   *   The bin the cache item was stored in.
-   * @return
-   *   TRUE on pass, FALSE on fail.
-   */
-  protected function checkCacheExists($cid, $var, $bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-
-    $cache = cache_get($cid, $bin);
-
-    return isset($cache->data) && $cache->data == $var;
-  }
-
-  /**
-   * Assert or a cache entry exists.
-   *
-   * @param $message
-   *   Message to display.
-   * @param $var
-   *   The variable the cache should contain.
-   * @param $cid
-   *   The cache id.
-   * @param $bin
-   *   The bin the cache item was stored in.
-   */
-  protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-    if ($cid == NULL) {
-      $cid = $this->default_cid;
-    }
-    if ($var == NULL) {
-      $var = $this->default_value;
-    }
-
-    $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
-  }
-
-  /**
-   * Assert or a cache entry has been removed.
-   *
-   * @param $message
-   *   Message to display.
-   * @param $cid
-   *   The cache id.
-   * @param $bin
-   *   The bin the cache item was stored in.
-   */
-  function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-    if ($cid == NULL) {
-      $cid = $this->default_cid;
-    }
-
-    $cache = cache_get($cid, $bin);
-    $this->assertFalse($cache, $message);
-  }
-
-  /**
-   * Perform the general wipe.
-   * @param $bin
-   *   The bin to perform the wipe on.
-   */
-  protected function generalWipe($bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-
-    cache_clear_all(NULL, $bin);
-  }
-
-  /**
-   * Setup the lifetime settings for caching.
-   *
-   * @param $time
-   *   The time in seconds the cache should minimal live.
-   */
-  protected function setupLifetime($time) {
-    variable_set('cache_lifetime', $time);
-    variable_set('cache_flush', 0);
-  }
-}
-
-class MemCacheSavingCase extends MemcacheTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Memcache saving test',
-      'description' => 'Check our variables are saved and restored the right way.',
-      'group' => 'Memcache'
-    );
-  }
-
-  /**
-   * Test the saving and restoring of a string.
-   */
-  function testString() {
-    $this->checkVariable($this->randomName(100));
-  }
-
-  /**
-   * Test the saving and restoring of an integer.
-   */
-  function testInteger() {
-    $this->checkVariable(100);
-  }
-
-  /**
-   * Test the saving and restoring of a double.
-   */
-  function testDouble() {
-    $this->checkVariable(1.29);
-  }
-
-  /**
-   * Test the saving and restoring of an array.
-   */
-  function testArray() {
-    $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
-  }
-
-  /**
-   * Test the saving and restoring of an object.
-   */
-  function testObject() {
-    $test_object = new stdClass();
-    $test_object->test1 = $this->randomName(100);
-    $test_object->test2 = 100;
-    $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
-
-    cache_set('test_object', $test_object, 'cache');
-    $cache = cache_get('test_object', 'cache');
-    $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
-  }
-
-  /*
-   * Check or a variable is stored and restored properly.
-   **/
-  function checkVariable($var) {
-    cache_set('test_var', $var, 'cache');
-    $cache = cache_get('test_var', 'cache');
-    $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
-  }
-}
-
-/**
- * Test cache_get_multiple().
- */
-class MemCacheGetMultipleUnitTest extends MemcacheTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Fetching multiple cache items',
-      'description' => 'Confirm that multiple records are fetched correctly.',
-      'group' => 'Memcache',
-    );
-  }
-
-  function setUp() {
-    $this->default_bin = 'cache_page';
-    parent::setUp();
-  }
-
-  /**
-   * Test cache_get_multiple().
-   */
-  function testCacheMultiple() {
-    $item1 = $this->randomName(10);
-    $item2 = $this->randomName(10);
-    cache_set('item1', $item1, $this->default_bin);
-    cache_set('item2', $item2, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.'));
-    $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.'));
-
-    // Fetch both records from the database with cache_get_multiple().
-    $item_ids = array('item1', 'item2');
-    $items = cache_get_multiple($item_ids, $this->default_bin);
-    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
-    $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.'));
-
-    // Remove one item from the cache.
-    cache_clear_all('item2', $this->default_bin);
-
-    // Confirm that only one item is returned by cache_get_multiple().
-    $item_ids = array('item1', 'item2');
-    $items = cache_get_multiple($item_ids, $this->default_bin);
-    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
-    $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.'));
-    $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
-  }
-}
-
-/**
- * Test cache clearing methods.
- */
-class MemCacheClearCase extends MemcacheTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Cache clear test',
-      'description' => 'Check our clearing is done the proper way.',
-      'group' => 'Memcache'
-    );
-  }
-
-  function setUp() {
-    $this->default_bin = 'cache_page';
-    $this->default_value = $this->randomName(10);
-
-    parent::setUp();
-  }
-
-  /**
-   * Test clearing using a cid.
-   */
-  function testClearCid() {
-    cache_set('test_cid_clear', $this->default_value, $this->default_bin);
-
-    $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
-    cache_clear_all('test_cid_clear', $this->default_bin);
-
-    $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
-
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches were created for checking cid "*" with wildcard false.'));
-    cache_clear_all('*', $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches still exists after clearing cid "*" with wildcard false.'));
-  }
-
-  /**
-   * Test clearing using wildcard.
-   */
-  function testClearWildcard() {
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches were created for checking cid "*" with wildcard true.'));
-    cache_clear_all('*', $this->default_bin, TRUE);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches removed after clearing cid "*" with wildcard true.'));
-
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches were created for checking cid substring with wildcard true.'));
-    cache_clear_all('test_', $this->default_bin, TRUE);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches removed after clearing cid substring with wildcard true.'));
-  }
-
-  /**
-   * Test clearing using an array.
-   */
-  function testClearArray() {
-    // Create three cache entries.
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear3', $this->default_value),
-                      t('Three cache entries were created.'));
-
-    // Clear two entries using an array.
-    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                       t('Two cache entries removed after clearing with an array.'));
-
-    $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
-                      t('Entry was not cleared from the cache'));
-
-    // Set the cache clear threshold to 2 to confirm that the full bin is cleared
-    // when the threshold is exceeded.
-    variable_set('cache_clear_threshold', 2);
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two cache entries were created.'));
-    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear2', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear3', $this->default_value),
-                       t('All cache entries removed when the array exceeded the cache clear threshold.'));
-  }
-}
-
-/**
- * @file
- * Provides SimpleTests for core session handling functionality.
- */
-class MemcacheSessionTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Session tests',
-      'description' => 'Memcache session handling tests.',
-      'group' => 'Memcache'
-    );
-  }
-
-  function setUp() {
-    parent::setUp('session_test');
-  }
-
-  /**
-   * Tests for drupal_save_session() and drupal_session_regenerate().
-   */
-  function testSessionSaveRegenerate() {
-    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session'));
-    $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
-    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
-    $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
-    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
-
-    // Test session hardening code from SA-2008-044.
-    $user = $this->drupalCreateUser(array('access content'));
-
-    // Enable sessions.
-    $this->sessionReset($user->uid);
-
-    // Make sure the session cookie is set as HttpOnly.
-    $this->drupalLogin($user);
-    $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.'));
-    $this->drupalLogout();
-
-    // Verify that the session is regenerated if a module calls exit
-    // in hook_user_login().
-    user_save($user, array('name' => 'session_test_user'));
-    $user->name = 'session_test_user';
-    $this->drupalGet('session-test/id');
-    $matches = array();
-    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
-    $this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
-    $original_session = $matches[1];
-
-    // We cannot use $this->drupalLogin($user); because we exit in
-    // session_test_user_login() which breaks a normal assertion.
-    $edit = array(
-      'name' => $user->name,
-      'pass' => $user->pass_raw
-    );
-    $this->drupalPost('user', $edit, t('Log in'));
-    $this->drupalGet('user');
-    $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
-    $this->_logged_in = $pass;
-
-    $this->drupalGet('session-test/id');
-    $matches = array();
-    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
-    $this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
-    $this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));
-  }
-
-  /**
-   * Test data persistence via the session_test module callbacks. Also tests
-   * drupal_session_count() since session data is already generated here.
-   */
-  function testDataPersistence() {
-    $user = $this->drupalCreateUser(array('access content'));
-    // Enable sessions.
-    $this->sessionReset($user->uid);
-
-    $this->drupalLogin($user);
-
-    $value_1 = $this->randomName();
-    $this->drupalGet('session-test/set/' . $value_1);
-    $this->assertText($value_1, t('The session value was stored.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));
-
-    // Attempt to write over val_1. If drupal_save_session(FALSE) is working.
-    // properly, val_1 will still be set.
-    $value_2 = $this->randomName();
-    $this->drupalGet('session-test/no-set/' . $value_2);
-    $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
-
-    // Switch browser cookie to anonymous user, then back to user 1.
-    $this->sessionReset();
-    $this->sessionReset($user->uid);
-    $this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
-
-    // Logout the user and make sure the stored value no longer persists.
-    $this->drupalLogout();
-
-    $this->sessionReset();
-    $this->drupalGet('session-test/get');
-    $this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
-
-    // Now try to store some data as an anonymous user.
-    $value_3 = $this->randomName();
-    $this->drupalGet('session-test/set/' . $value_3);
-    $this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
-
-    // Try to store data when drupal_save_session(FALSE).
-    $value_4 = $this->randomName();
-    $this->drupalGet('session-test/no-set/' . $value_4);
-    $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
-
-    // Login, the data should persist.
-    $this->drupalLogin($user);
-    $this->sessionReset($user->uid);
-    $this->drupalGet('session-test/get');
-    $this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
-
-    // Change session and create another user.
-    $user2 = $this->drupalCreateUser(array('access content'));
-    $this->sessionReset($user2->uid);
-    $this->drupalLogin($user2);
-  }
-
-  /**
-   * Test that empty anonymous sessions are destroyed.
-   */
-  function testEmptyAnonymousSession() {
-    // Verify that no session is automatically created for anonymous user.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-
-    // The same behavior is expected when caching is enabled.
-    variable_set('cache', CACHE_NORMAL);
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
-
-    // Start a new session by setting a message.
-    $this->drupalGet('session-test/set-message');
-    $this->assertSessionCookie(TRUE);
-    $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
-
-    // Display the message, during the same request the session is destroyed
-    // and the session cookie is unset.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(FALSE);
-    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
-    $this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
-    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
-
-    // Verify that session was destroyed.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-    $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
-
-    // Verify that no session is created if drupal_save_session(FALSE) is called.
-    $this->drupalGet('session-test/set-message-but-dont-save');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-
-    // Verify that no message is displayed.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.'));
-  }
-
-  /**
-   * Reset the cookie file so that it refers to the specified user.
-   *
-   * @param $uid User id to set as the active session.
-   */
-  function sessionReset($uid = 0) {
-    // Close the internal browser.
-    $this->curlClose();
-    $this->loggedInUser = FALSE;
-
-    // Change cookie file for user.
-    $this->cookieFile = file_directory_path('temporary') . '/cookie.' . $uid . '.txt';
-    $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile;
-    $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE;
-    $this->drupalGet('session-test/get');
-    $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
-  }
-
-  /**
-   * Assert whether the SimpleTest browser sent a session cookie.
-   */
-  function assertSessionCookie($sent) {
-    if ($sent) {
-      $this->assertNotNull($this->session_id, t('Session cookie was sent.'));
-    }
-    else {
-      $this->assertNull($this->session_id, t('Session cookie was not sent.'));
-    }
-  }
-
-  /**
-   * Assert whether $_SESSION is empty at the beginning of the request.
-   */
-  function assertSessionEmpty($empty) {
-    if ($empty) {
-      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
-    }
-    else {
-      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
-    }
-  }
-}
diff --git a/sites/all/modules/memcache/memcache_admin/memcache_admin.info b/sites/all/modules/memcache/memcache_admin/memcache_admin.info
index b9d8fcb66915a18065b28ad1033a309af0b77e40..7d88755380fed384d1a082e652f61526853561c5 100644
--- a/sites/all/modules/memcache/memcache_admin/memcache_admin.info
+++ b/sites/all/modules/memcache/memcache_admin/memcache_admin.info
@@ -1,12 +1,11 @@
 name = Memcache Admin
 description = Adds a User Interface to monitor the Memcache for this site.
-package = Caching
 core = 7.x
-files[] = memcache_admin.module
+package = Performance and scalability
 
-; Information added by drupal.org packaging script on 2011-05-27
-version = "7.x-1.0-beta4"
+; Information added by drupal.org packaging script on 2012-01-07
+version = "7.x-1.0-rc3"
 core = "7.x"
 project = "memcache"
-datestamp = "1306500116"
+datestamp = "1325917859"
 
diff --git a/sites/all/modules/memcache/memcache_admin/memcache_admin.install b/sites/all/modules/memcache/memcache_admin/memcache_admin.install
new file mode 100644
index 0000000000000000000000000000000000000000..0622b206d4a156f5d8d91da89282a5d91fd52bab
--- /dev/null
+++ b/sites/all/modules/memcache/memcache_admin/memcache_admin.install
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file update functions for memcache_admin.
+ */
+
+/**
+ * Flush caches and rebuild menu to allow new stats pages to appear.
+ */
+function memcache_admin_update_7001() {
+  drupal_flush_all_caches();
+  menu_rebuild();
+}
diff --git a/sites/all/modules/memcache/memcache_admin/memcache_admin.module b/sites/all/modules/memcache/memcache_admin/memcache_admin.module
index ee84db5cbb0e32c51e8e9fc714485909e2b19fdd..9760668efcc36f7334e0232ce1eb923ddc0b6696 100644
--- a/sites/all/modules/memcache/memcache_admin/memcache_admin.module
+++ b/sites/all/modules/memcache/memcache_admin/memcache_admin.module
@@ -7,7 +7,7 @@
  */
 function memcache_admin_init() {
   global $user;
-  if (($user->uid == 0) || strstr($_SERVER['PHP_SELF'], 'update.php') || strstr($_GET['q'], 'autocomplete')) {
+  if (($user->uid == 0) || strstr($_SERVER['PHP_SELF'], 'update.php') || (isset($_GET['q']) && (in_array($_GET['q'], array('upload/js', 'admin/content/node-settings/rebuild')) || substr($_GET['q'], 0, strlen('system/files')) == 'system/files' || substr($_GET['q'], 0, strlen('batch')) == 'batch' || strstr($_GET['q'], 'autocomplete')))) {
     // update.php relies on standard error handler
   }
   else {
@@ -19,18 +19,22 @@ function memcache_admin_init() {
 }
 
 /**
- * Implementation of hook_perm().
+ * Implements hook_perm().
  */
 function memcache_admin_permission() {
   return array(
     'access memcache statistics' => array(
       'title' => t('Access memcache statistics'),
     ),
+    'access slab cachedump' => array(
+      'title' => t('Access cachedump of memcache slab'),
+      'restrict access' => TRUE,
+    ),
   );
 }
 
 /**
- * Implementation of hook_menu().
+ * Implements hook_menu().
  */
 function memcache_admin_menu() {
   $items['admin/config/system/memcache'] = array(
@@ -41,15 +45,15 @@ function memcache_admin_menu() {
     'access arguments' => array('administer site configuration'),
   );
   $items['admin/reports/memcache'] = array(
-    'title' => 'Memcache status',
-    'description' => "View the statistics for this site's memcache.",
+    'title' => 'Memcache statistics',
+    'description' => "View statistics for all configured memcache servers.",
     'page callback' => 'memcache_admin_stats',
     'access arguments' => array('access memcache statistics'),
     'weight' => 1,
   );
-  $memache_servers = variable_get('memcache_servers', array());
+  $memcache_servers = variable_get('memcache_servers', array('127.0.0.1:11211' => 'default'));
   $clusters = array();
-  foreach($memache_servers as $server => $cluster) {
+  foreach ($memcache_servers as $server => $cluster) {
     $clusters[$cluster]['servers'][] = $server;
     $clusters[$cluster]['bin'] = _memcache_admin_get_bin_for_cluster($cluster);
   }
@@ -70,21 +74,25 @@ function memcache_admin_menu() {
         'page callback' => 'memcache_admin_stats',
         'page arguments' => array($cluster),
         'access arguments' => array('access memcache statistics'),
-        'weight' => $count,
+        'weight' => $count++,
       );
-      $count++;
-
-      $sub_count = 0;
-      foreach (array('default', 'malloc', 'maps', 'slabs', 'items', 'sizes') as $type) {
-        $items['admin/reports/memcache/'. $cluster .'/'. $type] = array(
-          'type' => $type == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
-          'page callback' => 'memcache_admin_stats',
-          'page arguments' => array($cluster, $type),
-          'title' => $type,
+      foreach ($cluster_info['servers'] as $server) {
+        $items["admin/reports/memcache/$cluster/$server"] = array(
+          'title' => check_plain($server),
+          'type' =>  MENU_CALLBACK,
+          'page callback' => 'memcache_admin_stats_raw',
+          'page arguments' => array($cluster, $server),
           'access arguments' => array('access memcache statistics'),
-          'weight' => $sub_count,
         );
-        $sub_count++;
+        foreach (memcache_admin_stats_types($cluster) as $type) {
+          $items["admin/reports/memcache/$cluster/$server/$type"] = array(
+            'type' => MENU_CALLBACK,
+            'page callback' => 'memcache_admin_stats_raw',
+            'page arguments' => array($cluster, $server, $type),
+            'title' => $type,
+            'access arguments' => array('access memcache statistics'),
+          );
+        }
       }
     }
   }
@@ -104,102 +112,392 @@ function memcache_admin_admin_settings() {
   return system_settings_form($form);
 }
 
+function _memcache_admin_default_bin($bin) {
+  if ($bin == 'default') {
+    return 'cache';
+  }
+  return $bin;
+}
+
+function _memcache_admin_stats_connections($stats) {
+  return t('!current open of !total total', array('!current' => number_format($stats['curr_connections']), '!total' => number_format($stats['total_connections'])));
+}
+
+/**
+ * Statistics report: calculate # of set cmds and total cmds.
+ */
+function _memcache_admin_stats_sets($stats) {
+  if (($stats['cmd_set'] + $stats['cmd_get']) == 0) {
+    $sets = 0;
+  }
+  else {
+    $sets = $stats['cmd_set'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
+  }
+  if (empty($stats['uptime'])) {
+    $average = 0;
+  }
+  else {
+    $average = $sets / $stats['uptime'];
+  }
+  return t('!average/s; !set sets (!sets%) of !total commands', array('!average' => number_format($average, 2), '!sets' => number_format($sets, 2), '!set' => number_format($stats['cmd_set']), '!total' => number_format($stats['cmd_set'] + $stats['cmd_get'])));
+}
+
+/**
+ * Statistics report: calculate # of get cmds, broken down by hits and misses.
+ */
+function _memcache_admin_stats_gets($stats) {
+  if (($stats['cmd_set'] + $stats['cmd_get']) == 0) {
+    $gets = 0;
+  }
+  else {
+    $gets = $stats['cmd_get'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
+  }
+  if (empty($stats['uptime'])) {
+    $average = 0;
+  }
+  else {
+    $average = $stats['cmd_get'] / $stats['uptime'];
+  }
+  return t('!average/s; !total gets (!gets%); !hit hits (!percent_hit%) !miss misses (!percent_miss%)', array('!average' => number_format($average, 2), '!gets' => number_format($gets, 2), '!hit' => number_format($stats['get_hits']), '!percent_hit' => ($stats['cmd_get'] > 0 ? number_format($stats['get_hits'] / $stats['cmd_get'] * 100, 2) : '0.00'), '!miss' => number_format($stats['get_misses']), '!percent_miss' => ($stats['cmd_get'] > 0 ? number_format($stats['get_misses'] / $stats['cmd_get'] * 100, 2) : '0.00'), '!total' => number_format($stats['cmd_get'])));
+}
+
+/**
+ * Statistics report: calculate # of increments and decrements.
+ */
+function _memcache_admin_stats_counters($stats) {
+  return t('!incr increments, !decr decrements', array('!incr' => number_format($stats['incr_hits'] + $stats['incr_misses']), '!decr' => number_format($stats['decr_hits'] + $stats['decr_misses'])));
+}
+
 /**
- * Memcahe Stats page
+ * Statistics report: calculate bytes transferred.
+ */
+function _memcache_admin_stats_transfer($stats) {
+  if ($stats['bytes_written'] == 0) {
+    $written = 0;
+  }
+  else {
+    $written = $stats['bytes_read'] / $stats['bytes_written'] * 100;
+  }
+  return t('!to:!from (!written% to cache)', array('!to' => format_size((int)$stats['bytes_read']), '!from' => format_size((int)$stats['bytes_written']), '!written' => number_format($written, 2)));
+}
+
+/**
+ * Statistics report: calculate per-connection averages.
+ */
+function _memcache_admin_stats_average($stats) {
+  if ($stats['total_connections'] == 0) {
+    $get = 0;
+    $set = 0;
+    $read = 0;
+    $write = 0;
+  }
+  else {
+    $get = $stats['cmd_get'] / $stats['total_connections'];
+    $set = $stats['cmd_set'] / $stats['total_connections'];
+    $read = $stats['bytes_written'] / $stats['total_connections'];
+    $write = $stats['bytes_read'] / $stats['total_connections'];
+  }
+  return t('!read in !get gets; !write in !set sets', array('!get' => number_format($get, 2), '!set' => number_format($set, 2), '!read' => format_size(number_format($read, 2)), '!write' => format_size(number_format($write, 2))));
+}
+
+/**
+ * Statistics report: calculate available memory.
+ */
+function _memcache_admin_stats_memory($stats) {
+  if ($stats['limit_maxbytes'] == 0) {
+    $percent = 0;
+  }
+  else {
+    $percent = 100 - $stats['bytes'] / $stats['limit_maxbytes'] * 100;
+  }
+  return t('!available (!percent%) of !total', array('!available' => format_size($stats['limit_maxbytes'] - $stats['bytes']), '!percent' => number_format($percent, 2), '!total' => format_size($stats['limit_maxbytes'])));
+}
+
+/**
+ * Helper function, reverse map the memcache_bins variable.
+ */
+function memcache_admin_bin_mapping($bin = 'cache') {
+  $bins = array_flip(variable_get('memcache_bins', array('cache' => 'default')));
+  if (isset($bins[$bin])) {
+    return $bins[$bin];
+  }
+  else {
+    // The default bin is 'cache'.
+    return _memcache_admin_default_bin($bin);
+  }
+}
+
+/**
+ * Memcache Stats page
  *
- * @param string $cluster - which cluster to view?
- * @param string $type - which type of stat, eg: default, malloc, maps, cachedump, slabs, items or sizes
  * @return string
  */
-function memcache_admin_stats($cluster = 'default', $type = 'default') {
-  $bin = _memcache_admin_get_bin_for_cluster($cluster);
-
-  if ($bin) {
-    $stats = dmemcache_stats($bin, $type);
-
-    if (is_array($stats) && count($stats)) {
-      $output = '';
-
-      foreach ($stats as $server => $values) {
-        if (is_array($values)) {
-          // Do some custome value tweaks for specific stat page types.
-          switch ($type) {
-            case 'default' :
-              $values['uptime'] = format_interval($values['uptime']);
-              $values['time'] = format_date($values['time']);
-              $values['bytes'] = format_size($values['bytes']);
-              $values['bytes_read'] = format_size($values['bytes_read']);
-              $values['bytes_written'] = format_size($values['bytes_written']);
-              $values['limit_maxbytes'] = format_size($values['limit_maxbytes']);
-
-              //Custom Entries
-              $values['hit_percentage'] = ($values['cmd_get'] > 0) 
-                ? number_format(100.0 * $values['get_hits'] / $values['cmd_get'], 2) . '%' 
-                : '0';
-
-              $mem_used = intval($values['bytes']) / (intval($values['limit_maxbytes']) * 1024);
-              $values['mem_used'] = number_format(100.0 * $mem_used, 2) . '%';
-              break;
-          }
+function memcache_admin_stats($bin = 'cache') {
+  $bin = memcache_admin_bin_mapping($bin);
 
-          $output .= theme('memcache_admin_stats_table', array('server' => $server, 'stats' => $values));
-        }
-        else {
-          drupal_set_message(t('Unable to get statistic from server %server', array('%server' => $server)));
-        }
-      }
-    }
+  $stats = dmemcache_stats($bin, 'default', TRUE);
+
+  $memcache_servers = variable_get('memcache_servers', array('127.0.0.1:11211' => 'default'));
 
+  if (is_array($stats[$bin]) && count($stats[$bin])) {
+    $stats = $stats[$bin];
+
+    $mc = dmemcache_object($bin);
+    if ($mc instanceof Memcached) {
+      $version = t('Memcached v!version', array('!version' => phpversion('Memcached')));
+    }
+    elseif ($mc instanceof Memcache) {
+      $version = t('Memcache v!version', array('!version' => phpversion('Memcache')));
+    }
     else {
-      $output = '';
-      drupal_set_message(t('No available statistics for this bin.'));
+      $version = t('Unknown');
+      drupal_set_message(t('Failed to detect the memcache PECL extension.'), 'error');
+    }
+    // Building per-server stats for the current memcache bin.
+    $servers = array();
+    foreach ($memcache_servers as $server => $b) {
+      $b = memcache_admin_bin_mapping($b);
+      if ($b == $bin) {
+        $servers[] = $server;
+
+        if (empty($stats[$server]['uptime'])) {
+          drupal_set_message(t('Failed to connect to server at %server.', array('%server' => $server)), 'error');
+        }
+        $data['server_overview'][$server] = t('v!version running !uptime', array('!version' => check_plain($stats[$server]['version']), '!uptime' => format_interval($stats[$server]['uptime'])));
+        $data['server_pecl'][$server] = t('n/a');
+        $data['server_time'][$server] = format_date($stats[$server]['time']);
+        $data['server_connections'][$server] = _memcache_admin_stats_connections($stats[$server]);
+        $data['cache_sets'][$server] = _memcache_admin_stats_sets($stats[$server]);
+        $data['cache_gets'][$server] = _memcache_admin_stats_gets($stats[$server]);
+        $data['cache_counters'][$server] = _memcache_admin_stats_counters($stats[$server]);
+        $data['cache_transfer'][$server] = _memcache_admin_stats_transfer($stats[$server]);
+        $data['cache_average'][$server] = _memcache_admin_stats_average($stats[$server]);
+        $data['memory_available'][$server] = _memcache_admin_stats_memory($stats[$server]);
+        $data['memory_evictions'][$server] = number_format($stats[$server]['evictions']);
+      }
     }
+    // Building a report as a custom formatted array of arrays that gets
+    // properly displayed by theme_memcache_admin_stats_table.
+    $report = array(
+      'Server overview' => array(
+        array_merge(
+          array('header' => t('Uptime')),
+          array('total' => t('n/a')),
+          $data['server_overview']),
+        array_merge(
+          array('header' => t('PECL extension')),
+          array('total' => $version),
+          $data['server_pecl']),
+        array_merge(
+          array('header' => t('Server time')),
+          array('total' => t('n/a')),
+          $data['server_time']),
+        array_merge(
+          array('header' => t('Connections')),
+          array('total' => _memcache_admin_stats_connections($stats['total'])),
+          $data['server_connections']),
+      ),
+      'Cache statistics' => array(
+        array_merge(
+          array('header' => t('Sets')),
+          array('total' => _memcache_admin_stats_sets($stats['total'])),
+          $data['cache_sets']),
+        array_merge(
+          array('header' => t('Gets')),
+          array('total' => _memcache_admin_stats_gets($stats['total'])),
+          $data['cache_gets']),
+        array_merge(
+          array('header' => t('Counters')),
+          array('total' => _memcache_admin_stats_counters($stats['total'])),
+          $data['cache_counters']),
+        array_merge(
+          array('header' => t('Transferred')),
+          array('total' => _memcache_admin_stats_transfer($stats['total'])),
+          $data['cache_transfer']),
+        array_merge(
+          array('header' => t('Per-connection average')),
+          array('total' => _memcache_admin_stats_average($stats['total'])),
+          $data['cache_average']),
+      ),
+      'Memory overview' => array(
+        array_merge(
+          array('header' => t('Available memory')),
+          array('total' => _memcache_admin_stats_memory($stats['total'])),
+          $data['memory_available']),
+        array_merge(
+          array('header' => t('Evictions')),
+          array('total' => $stats['total']['evictions']),
+          $data['memory_evictions']),
+      ),
+    );
+    $output = theme('memcache_admin_stats_table', array('bin' => $bin, 'servers' => $servers, 'report' => $report));
+  }
+  else {
+    $output = '';
+    drupal_set_message(t('There are no statistics being reported for this bin.'), 'error');
   }
 
   return $output;
 }
 
+function memcache_admin_stats_raw($bin, $server, $type = 'default') {
+  $cluster = memcache_admin_bin_mapping($bin);
+  $slab = (int)arg(7);
+  if (arg(6) == 'cachedump' && !empty($slab) && user_access('access slab cachedump')) {
+    $stats = dmemcache_stats($cluster, arg(7), FALSE);
+  }
+  else {
+    $stats = dmemcache_stats($cluster, $type, FALSE);
+  }
+  $breadcrumbs = array(l(t('Home'), NULL), l(t('Administer'), 'admin'), l(t('Reports'), 'admin/reports'), l(t('Memcache'), 'admin/reports/memcache'), l(t($bin), "admin/reports/memcache/$bin"));
+  if ($type == 'slabs' && arg(6) == 'cachedump' && user_access('access slab cachedump')) {
+    $breadcrumbs[] = l($server, "admin/reports/memcache/$bin/$server");
+    $breadcrumbs[] = l('slabs', "admin/reports/memcache/$bin/$server/$type");
+  }
+  drupal_set_breadcrumb($breadcrumbs);
+  if (isset($stats[$cluster][$server]) && is_array($stats[$cluster][$server]) && count($stats[$cluster][$server])) {
+    $output = theme('memcache_admin_stats_raw_table', array('cluster' => $cluster, 'server' => $server, 'stats' => $stats[$cluster][$server], 'type' => $type));
+  }
+  elseif ($type == 'slabs' && is_array($stats[$cluster]) && count($stats[$cluster])) {
+    $output = theme('memcache_admin_stats_raw_table', array('cluster' => $cluster, 'server' => $server, 'stats' => $stats[$cluster], 'type' => $type));
+  }
+  else {
+    $output = theme('memcache_admin_stats_raw_table', array('cluster' => $cluster, 'server' => $server, 'stats' => array(), 'type' => $type));
+    drupal_set_message(t('No @type statistics for this bin.', array('@type' => $type)));
+  }
+  return $output;
+}
+
 /**
- * Implementation of hook_theme().
+ * Implements hook_theme().
  */
 function memcache_admin_theme() {
   return array(
     'memcache_admin_stats_table' => array(
-      'variables' => array('server' => NULL, 'stats' => NULL),
+      'variables' => array('bin' => NULL, 'servers' => NULL, 'report' => NULL),
+    ),
+    'memcache_admin_stats_raw_table' => array(
+      'variables' => array('bin' => NULL, 'server' => NULL, 'stats' => NULL, 'type' => NULL),
     )
   );
 }
 
 /**
  * Theme function for rendering the output from memcache_admin_stats
- *
- * @param string $server - Server name:port for caption for the table
- * @param array $stats - array of key/value string pairs for the table results
- * @return string
  */
 function theme_memcache_admin_stats_table($variables) {
+  $bin = $variables['bin'];
+  $servers = $variables['servers'];
+  $stats = $variables['report'];
+
+  $output = '';
+  $links = array();
+  $memcache_bins = variable_get('memcache_bins', array('cache' => 'default'));
+  foreach ($servers as $server) {
+    $link_bin = $memcache_bins[$bin];
+    $links[] = l($server, check_plain("admin/reports/memcache/$link_bin/$server"));
+  }
+  $headers = array_merge(array('', t('Totals')), $links);
+  foreach ($stats as $table => $data) {
+    $rows = array();
+    foreach ($data as $row) {
+      if (isset($row[2]) && is_array($row[2])) {
+        $row[2] = implode(', ', $row[2]);
+      }
+      else {
+        $row[2] = '';
+      }
+      $rows[] = $row;
+    }
+    $output .= theme('table', array('header' => $headers, 'rows' => $rows));
+  }
+  return $output;
+}
+
+function memcache_admin_stats_types($bin) {
+  module_load_include('inc', 'memcache', 'dmemcache');
+  if ($mc = dmemcache_object($bin)) {
+    if ($mc instanceof Memcache) {
+      // TODO: Determine which versions of the PECL memcache extension have
+      // these other stats types: 'malloc', 'maps', optionally detect this
+      // version and expose them.  These stats are "subject to change without
+      // warning" unfortunately.
+      return array('default', 'slabs', 'items', 'sizes');
+    }
+    else {
+      // The Memcached PECL extension only offers the default statistics.
+      return array('default');
+    }
+  }
+  else {
+    return array();
+  }
+}
+ 
+function theme_memcache_admin_stats_raw_table($variables) {
+  $cluster = $variables['cluster'];
   $server = $variables['server'];
   $stats = $variables['stats'];
+  $current_type = isset($variables['type']) ? $variables['type'] : 'default';
 
-  $rows = array();
+  $memcache_bins = variable_get('memcache_bins', array());
+  $bin = isset($memcache_bins[$cluster]) ? $memcache_bins[$cluster] : 'default';
+  // Provide navigation for the various memcache stats types
+  if (count(memcache_admin_stats_types($bin)) > 1) {
+    foreach (memcache_admin_stats_types($bin) as $type) {
+      if ($current_type == $type) {
+        $links[] = '<strong>' . l(t($type), "admin/reports/memcache/$bin/$server/". ($type == 'default' ? '' : $type)) .'</strong>';
+      }
+      else {
+        $links[] = l(t($type), "admin/reports/memcache/$bin/$server/". ($type == 'default' ? '' : $type));
+      }
+    }
+  }
+  $output = !empty($links) ? implode($links, ' | ') : '';
 
+  $headers = array(t('Property'), t('Value'));
+  $rows = array();
+  // Items are returned as an array within an array within an array.  We step
+  // in one level to properly display the contained statistics.
+  if ($current_type == 'items' && isset($stats['items'])) {
+    $stats = $stats['items'];
+  }
   foreach ($stats as $key => $value) {
+    // Add navigation for getting a cachedump of individual slabs
+    if (($current_type == 'slabs' || $current_type == 'items') && is_int($key) && user_access('access slab cachedump')) {
+      $key = l($key, "admin/reports/memcache/$bin/$server/slabs/cachedump/$key");
+    }
     if (is_array($value)) {
       $rs = array();
       foreach ($value as $k => $v) {
-        $rs[] = array($k, $v);
-      }
-      $rows[] = array($key, theme('table', array('header' => array(), 'rows' => $rs)));
+        // Format timestamp when viewing cachedump of individual slabs.
+        if ($current_type == 'slabs' && user_access('access slab cachedump') && arg(6) == 'cachedump' && $k == 0) {
+          $k = t('Size');
+          $v = format_size($v);
+        }
+        else if ($current_type == 'slabs' && user_access('access slab cachedump') && arg(6) == 'cachedump' && $k == 1) {
+          $k = t('Expire');
+          $full_stats = dmemcache_stats($cluster, 'default');
+          $infinite = $full_stats[$cluster][$server]['time'] - $full_stats[$cluster][$server]['uptime'];
+          if ($v == $infinite) {
+            $v = t('infinite');
+          }
+          else {
+            $v = t('in @time', array('@time' => format_interval($v - time())));
+          }
+        }
+        $rs[] = array(check_plain($k), check_plain($v));
+       }
+      $rows[] = array($key, theme('table', array('rows' => $rs)));
     }
     else {
-      $rows[] = array($key, $value);
+      $rows[] = array(check_plain($key), check_plain($value));
     }
   }
-
-  return theme('table', array('header' => array(t('Property'), t('Value')), 'rows' => $rows, 'caption' => $server));
+  $output .= theme('table', array('header' => $headers, 'rows' => $rows));
+  return $output;
 }
 
-
 /**
  * Retrieve the cluster for any given bin
  *
@@ -210,8 +508,8 @@ function _memcache_admin_get_bin_for_cluster($cluster) {
   static $cluster_map = array();
 
   if (!isset($cluster_map[$cluster])) {
-    $memache_bins = variable_get('memcache_bins', array());
-    if ($mapping = array_search($cluster, $memache_bins)) {
+    $memcache_bins = variable_get('memcache_bins', array());
+    if ($mapping = array_search($cluster, $memcache_bins)) {
       $cluster_map[$cluster] = $mapping;
     }
     else {
@@ -229,6 +527,14 @@ function _memcache_admin_get_bin_for_cluster($cluster) {
 function memcache_admin_shutdown() {
   global $_memcache_statistics;
 
+  // Don't call theme() during shutdown if the registry has been rebuilt (such
+  // as when enabling/disabling modules on admin/build/modules) as things break.
+  // Instead, simply exit without displaying admin statistics for this page
+  // load.  See http://drupal.org/node/616282 for discussion.
+  if (!function_exists('theme_get_registry') || !theme_get_registry()) {
+    return;
+  }
+
   // Try not to break non-HTML pages.
   if (function_exists('drupal_get_http_header')) {
     $header = drupal_get_http_header('content-type');
diff --git a/sites/all/modules/memcache/tests/memcache-lock.test b/sites/all/modules/memcache/tests/memcache-lock.test
new file mode 100644
index 0000000000000000000000000000000000000000..4bfff7b10371419249a7ca3c6020c04c23d79904
--- /dev/null
+++ b/sites/all/modules/memcache/tests/memcache-lock.test
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Tests for the lock system.
+ */
+class MemcacheLockFunctionalTest extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Locking framework tests',
+      'description' => 'Confirm locking works between two separate requests.',
+      'group' => 'Memcache',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('memcache_test');
+  }
+
+  /**
+   * Confirm that we can acquire and release locks in two parallel requests.
+   */
+  function testLockAcquire() {
+    // Nasty hack. There are two processes involved here - the process
+    // calling the test itself, that calls lock_acquire() directly. And the
+    // 'child' process that is requested over http by drupalGet().
+    // In dmemcache.inc, we look for simpletest installs, to force a
+    // simpletest prefix as part of the memcache key - this avoids the
+    // actual Drupal site install being corrupted by entries from the tested
+    // site. However, the child install apparently does not set the simpletest
+    // global, so when making requests to that site, we pass the simpletest ID
+    // as part of the URL, and mess around with the globals on that side.
+    $test_run_id = $GLOBALS['drupal_test_info']['test_run_id'];
+    $lock_acquired = 'TRUE: Lock successfully acquired in memcache_test_lock_acquire()';
+    $lock_not_acquired = 'FALSE: Lock not acquired in memcache_test_lock_acquire()';
+    $this->assertTrue(lock_acquire('memcache_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
+    $this->assertTrue(lock_acquire('memcache_test_lock_acquire'), t('Lock extended by this request.'), t('Lock'));
+    lock_release('memcache_test_lock_acquire');
+
+    // Cause another request to acquire the lock.
+    $this->drupalGet('memcache-test/lock-acquire' . "/$test_run_id");
+    $this->assertText($lock_acquired, t('Lock acquired by the other request.'), t('Lock'));
+    // The other request has finished, thus it should have released its lock.
+    $this->assertTrue(lock_acquire('memcache_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
+    // This request holds the lock, so the other request cannot acquire it.
+    $this->drupalGet('memcache-test/lock-acquire' . "/$test_run_id");
+    $this->assertText($lock_not_acquired, t('Lock not acquired by the other request.'), t('Lock'));
+    lock_release('memcache_test_lock_acquire');
+
+    // Try a very short timeout and lock breaking.
+    $this->assertTrue(lock_acquire('memcache_test_lock_acquire', 1), t('Lock acquired by this request.'), t('Lock'));
+    sleep(2);
+    // The other request should break our lock.
+    $this->drupalGet('memcache-test/lock-acquire' . "/$test_run_id");
+    $this->assertText($lock_acquired, t('Lock acquired by the other request, breaking our lock.'), t('Lock'));
+    // We cannot renew it, since the other thread took it.
+    // @todo: this assertion currently fails - the lock_acquire() call returns
+    // true. For now, commented out the assertion, uncomment when attempting to
+    // fix.
+    //$this->assertFalse(lock_acquire('memcache_test_lock_acquire'), t('Lock cannot be extended by this request.'), t('Lock'));
+
+    // Check the shut-down function.
+    $lock_acquired_exit = 'TRUE: Lock successfully acquired in memcache_test_lock_exit()';
+    $lock_not_acquired_exit = 'FALSE: Lock not acquired in memcache_test_lock_exit()';
+    $this->drupalGet('memcache-test/lock-exit' . "/$test_run_id");
+    $this->assertText($lock_acquired_exit, t('Lock acquired by the other request before exit.'), t('Lock'));
+    $this->assertTrue(lock_acquire('memcache_test_lock_exit'), t('Lock acquired by this request after the other request exits.'), t('Lock'));
+  }
+
+}
diff --git a/sites/all/modules/memcache/tests/memcache-session.test b/sites/all/modules/memcache/tests/memcache-session.test
new file mode 100644
index 0000000000000000000000000000000000000000..26265a224fcfb271db57e6ec9cd195d775adb8f0
--- /dev/null
+++ b/sites/all/modules/memcache/tests/memcache-session.test
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * @file
+ * Provides SimpleTests for core session handling functionality.
+ */
+
+class MemcacheSessionTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Session tests',
+      'description' => 'Memcache session handling tests.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('session_test');
+  }
+
+  /**
+   * Tests for drupal_save_session() and drupal_session_regenerate().
+   */
+  function testSessionSaveRegenerate() {
+    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session'));
+    $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
+    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
+    $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
+    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
+
+    // Test session hardening code from SA-2008-044.
+    $user = $this->drupalCreateUser(array('access content'));
+
+    // Enable sessions.
+    $this->sessionReset($user->uid);
+
+    // Make sure the session cookie is set as HttpOnly.
+    $this->drupalLogin($user);
+    $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.'));
+    $this->drupalLogout();
+
+    // Verify that the session is regenerated if a module calls exit
+    // in hook_user_login().
+    user_save($user, array('name' => 'session_test_user'));
+    $user->name = 'session_test_user';
+    $this->drupalGet('session-test/id');
+    $matches = array();
+    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
+    $this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
+    $original_session = $matches[1];
+
+    // We cannot use $this->drupalLogin($user); because we exit in
+    // session_test_user_login() which breaks a normal assertion.
+    $edit = array(
+      'name' => $user->name,
+      'pass' => $user->pass_raw
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    $this->drupalGet('user');
+    $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
+    $this->_logged_in = $pass;
+
+    $this->drupalGet('session-test/id');
+    $matches = array();
+    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
+    $this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
+    $this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));
+  }
+
+  /**
+   * Test data persistence via the session_test module callbacks. Also tests
+   * drupal_session_count() since session data is already generated here.
+   */
+  function testDataPersistence() {
+    $user = $this->drupalCreateUser(array('access content'));
+    // Enable sessions.
+    $this->sessionReset($user->uid);
+
+    $this->drupalLogin($user);
+
+    $value_1 = $this->randomName();
+    $this->drupalGet('session-test/set/' . $value_1);
+    $this->assertText($value_1, t('The session value was stored.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));
+
+    // Attempt to write over val_1. If drupal_save_session(FALSE) is working.
+    // properly, val_1 will still be set.
+    $value_2 = $this->randomName();
+    $this->drupalGet('session-test/no-set/' . $value_2);
+    $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
+
+    // Switch browser cookie to anonymous user, then back to user 1.
+    $this->sessionReset();
+    $this->sessionReset($user->uid);
+    $this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
+
+    // Logout the user and make sure the stored value no longer persists.
+    $this->drupalLogout();
+
+    $this->sessionReset();
+    $this->drupalGet('session-test/get');
+    $this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
+
+    // Now try to store some data as an anonymous user.
+    $value_3 = $this->randomName();
+    $this->drupalGet('session-test/set/' . $value_3);
+    $this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
+
+    // Try to store data when drupal_save_session(FALSE).
+    $value_4 = $this->randomName();
+    $this->drupalGet('session-test/no-set/' . $value_4);
+    $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
+
+    // Login, the data should persist.
+    $this->drupalLogin($user);
+    $this->sessionReset($user->uid);
+    $this->drupalGet('session-test/get');
+    $this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
+
+    // Change session and create another user.
+    $user2 = $this->drupalCreateUser(array('access content'));
+    $this->sessionReset($user2->uid);
+    $this->drupalLogin($user2);
+  }
+
+  /**
+   * Test that empty anonymous sessions are destroyed.
+   */
+  function testEmptyAnonymousSession() {
+    // Verify that no session is automatically created for anonymous user.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+
+    // The same behavior is expected when caching is enabled.
+    variable_set('cache', TRUE);
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
+
+    // Start a new session by setting a message.
+    $this->drupalGet('session-test/set-message');
+    $this->assertSessionCookie(TRUE);
+    $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
+
+    // Display the message, during the same request the session is destroyed
+    // and the session cookie is unset.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(FALSE);
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
+    $this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
+    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
+
+    // Verify that session was destroyed.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
+
+    // Verify that no session is created if drupal_save_session(FALSE) is called.
+    $this->drupalGet('session-test/set-message-but-dont-save');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+
+    // Verify that no message is displayed.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.'));
+  }
+
+  /**
+   * Reset the cookie file so that it refers to the specified user.
+   *
+   * @param $uid User id to set as the active session.
+   */
+  function sessionReset($uid = 0) {
+    // Close the internal browser.
+    $this->curlClose();
+    $this->loggedInUser = FALSE;
+
+    // Change cookie file for user.
+    $this->cookieFile = variable_get('file_public_path', conf_path() . '/files') . '/cookie.' . $uid . '.txt';
+    $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile;
+    $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE;
+    $this->drupalGet('session-test/get');
+    $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
+  }
+
+  /**
+   * Assert whether the SimpleTest browser sent a session cookie.
+   */
+  function assertSessionCookie($sent) {
+    if ($sent) {
+      $this->assertNotNull($this->session_id, t('Session cookie was sent.'));
+    }
+    else {
+      $this->assertNull($this->session_id, t('Session cookie was not sent.'));
+    }
+  }
+
+  /**
+   * Assert whether $_SESSION is empty at the beginning of the request.
+   */
+  function assertSessionEmpty($empty) {
+    if ($empty) {
+      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
+    }
+    else {
+      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
+    }
+  }
+}
diff --git a/sites/all/modules/memcache/tests/memcache.test b/sites/all/modules/memcache/tests/memcache.test
new file mode 100644
index 0000000000000000000000000000000000000000..6cce36582bf7b98d6731fb48f5598037d178b57c
--- /dev/null
+++ b/sites/all/modules/memcache/tests/memcache.test
@@ -0,0 +1,572 @@
+<?php
+
+class MemcacheTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+  protected $default_bin = 'cache_memcache';
+  protected $default_cid = 'test_temporary';
+  protected $default_value = 'MemcacheTest';
+
+  function setUp() {
+    parent::setUp(func_get_args());
+
+    variable_set("cache_flush_$this->default_bin", 0);
+    variable_set('cache_class_cache_memcache', 'MemcacheDrupal');
+
+    $this->resetVariables();
+  }
+
+  /**
+   * Check whether or not a cache entry exists.
+   *
+   * @param $cid
+   *   The cache id.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function checkCacheExists($cid, $var, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    $cache = cache_get($cid, $bin);
+
+    return isset($cache->data) && $cache->data == $var;
+  }
+
+  /**
+   * Assert or a cache entry exists.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+    if ($var == NULL) {
+      $var = $this->default_value;
+    }
+
+    $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
+  }
+
+  /**
+   * Assert or a cache entry has been removed.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+
+    $cache = cache_get($cid, $bin);
+    $this->assertFalse($cache, $message);
+  }
+
+  /**
+   * Perform the general wipe.
+   * @param $bin
+   *   The bin to perform the wipe on.
+   */
+  protected function generalWipe($bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    cache_clear_all(NULL, $bin);
+  }
+
+  function resetVariables() {
+    _cache_get_object($this->default_bin)->reloadVariables();
+  }
+}
+
+class MemCacheSavingCase extends MemcacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Memcache saving test',
+      'description' => 'Check our variables are saved and restored the right way.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Test the saving and restoring of a string.
+   */
+  function testString() {
+    $this->checkVariable($this->randomName(100));
+  }
+
+  /**
+   * Test the saving and restoring of an integer.
+   */
+  function testInteger() {
+    $this->checkVariable(100);
+  }
+
+  /**
+   * Test the saving and restoring of a double.
+   */
+  function testDouble() {
+    $this->checkVariable(1.29);
+  }
+
+  /**
+   * Test the saving and restoring of an array.
+   */
+  function testArray() {
+    $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
+  }
+
+  /**
+   * Test the saving and restoring of an object.
+   */
+  function testObject() {
+    $test_object = new stdClass();
+    $test_object->test1 = $this->randomName(100);
+    $test_object->test2 = 100;
+    $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
+
+    cache_set('test_object', $test_object, 'cache');
+    $cache = cache_get('test_object', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
+  }
+
+  /*
+   * Check or a variable is stored and restored properly.
+   **/
+  function checkVariable($var) {
+    cache_set('test_var', $var, 'cache');
+    $cache = cache_get('test_var', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
+  }
+}
+
+/**
+ * Test cache_get_multiple().
+ */
+class MemCacheGetMultipleUnitTest extends MemcacheTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Fetching multiple cache items',
+      'description' => 'Confirm that multiple records are fetched correctly.',
+      'group' => 'Memcache',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Test cache_get_multiple().
+   */
+  function testCacheMultiple() {
+    $item1 = $this->randomName(10);
+    $item2 = $this->randomName(10);
+    cache_set('test:item1', $item1, $this->default_bin);
+    cache_set('test:item2', $item2, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test:item1', $item1), t('Item 1 is cached.'));
+    $this->assertTrue($this->checkCacheExists('test:item2', $item2), t('Item 2 is cached.'));
+
+    // Fetch both records from the database with cache_get_multiple().
+    $item_ids = array('test:item1', 'test:item2');
+    $items = cache_get_multiple($item_ids, $this->default_bin);
+    $this->assertEqual($items['test:item1']->data, $item1, t('Item was returned from cache successfully.'));
+    $this->assertEqual($items['test:item2']->data, $item2, t('Item was returned from cache successfully.'));
+
+    $this->assertTrue(empty($item_ids), t('Ids of returned items have been removed.'));
+
+    // Remove one item from the cache.
+    cache_clear_all('test:item2', $this->default_bin);
+
+    // Confirm that only one item is returned by cache_get_multiple().
+    $item_ids = array('test:item1', 'test:item2');
+    $items = cache_get_multiple($item_ids, $this->default_bin);
+    $this->assertEqual($items['test:item1']->data, $item1, t('Item was returned from cache successfully.'));
+    $this->assertFalse(isset($items['test:item2']), t('Item was not returned from the cache.'));
+    $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
+    $this->assertTrue(count($item_ids) == 1, t('Invalid cache ids still present.'));
+
+  }
+}
+
+/**
+ * Test cache clearing methods.
+ */
+class MemCacheClearCase extends MemcacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache clear test',
+      'description' => 'Check our clearing is done the proper way.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+
+    parent::setUp('memcache_test');
+    $this->default_value = $this->randomName(10);
+  }
+
+
+  /**
+   * Test clearing the cache with a cid, no cache lifetime.
+   */
+  function testClearCidNoLifetime() {
+    $this->clearCidTest();
+  }
+
+  /**
+   * Test clearing the cache with a cid, with cache lifetime.
+   */
+  function testClearCidLifetime() {
+    variable_set('cache_lifetime', 6000);
+    $this->clearCidTest();
+  }
+
+  /**
+   * Test clearing using wildcard prefixes, no cache lifetime.
+   */
+  function testClearWildcardNoLifetime() {
+    $this->clearWildcardPrefixTest();
+  }
+
+  /**
+   * Test clearing using wildcard prefix, with cache lifetime.
+   */
+  function testClearWildcardLifetime() {
+    variable_set('cache_lifetime', 6000);
+    $this->clearWildcardPrefixTest();
+  }
+
+  /**
+   * Test full bin flushes with no cache lifetime.
+   */
+  function testClearWildcardFull() {
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard true.'));
+    cache_clear_all('*', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid "*" with wildcard true.'));
+  }
+
+  /**
+   * Test full bin flushes with cache lifetime.
+   */
+  function testClearCacheLifetime() {
+    variable_set('cache_lifetime', 600);
+    $this->resetVariables();
+
+    // Set a cache item with an expiry.
+    cache_set('test_cid', $this->default_value, $this->default_bin, time() + 3600);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was created successfully.');
+
+    // Set a permanent cache item.
+    cache_set('test_cid_2', $this->default_value, $this->default_bin);
+
+    // Clear the page and block caches.
+    cache_clear_all(MEMCACHE_CONTENT_CLEAR, $this->default_bin);
+    // Since the cache was cleared within the current session, cache_get()
+    // should return false.
+    $this->assertFalse($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was cleared successfully.');
+
+    // However permament items should stay in place.
+    $this->assertTrue($this->checkCacheExists('test_cid_2', $this->default_value), 'Cache item was not cleared');
+
+    // If $_SESSION['cache_flush'] is not set, then the expired item should be returned.
+    unset($_SESSION['cache_flush']);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item is still returned due to minimum cache lifetime.');
+
+    // Set a much shorter cache lifetime.
+    variable_set('cache_content_flush_' . $this->default_bin, 0);
+    variable_set('cache_lifetime', 1);
+    cache_set('test_cid_1', $this->default_value, $this->default_bin, time() + 6000);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was created successfully.');
+    sleep(2);
+    cache_clear_all(MEMCACHE_CONTENT_CLEAR, $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid', $this->default_value), 'Cache item is not returned once minimum cache lifetime has expired.');
+
+    // Reset the cache clear variables.
+    variable_set('cache_content_flush_' . $this->default_bin, 0);
+    variable_set('cache_lifetime', 6000);
+    $this->resetVariables();
+    sleep(1);
+
+    // Confirm that cache_lifetime does not take effect for full bin flushes.
+    cache_set('test_cid', $this->default_value, $this->default_bin, time() + 6000);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was created successfully.');
+    cache_set('test_cid_2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_2', $this->default_value), 'Cache item was created successfully.');
+
+    // Now flush the bin.
+    cache_clear_all('*', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was cleared successfully.');
+    $this->assertFalse($this->checkCacheExists('test_cid_2', $this->default_value), 'Cache item was cleared successfully.');
+  }
+
+  /**
+   * Test different wildcards to verify the wildcard optimizations.
+   */
+  function testWildCardOptimizations() {
+
+    // Set and clear a cache with a short cid/wildcard.
+    cache_set('foo:1', $this->default_value, $this->default_bin);
+
+    $this->assertCacheExists(t('Foo cache was set.'), $this->default_value, 'foo:1');
+
+    cache_clear_all('foo', $this->default_bin, TRUE);
+    $this->assertCacheRemoved(t('Foo cache was invalidated.'), 'foo:1');
+
+    // Set additional longer caches.
+    cache_set('foobar', $this->default_value, $this->default_bin);
+    cache_set('foofoo', $this->default_value, $this->default_bin);
+
+    $this->assertCacheExists(t('Foobar cache set.'), $this->default_value, 'foobar');
+    $this->assertCacheExists(t('Foofoo cache set.'), $this->default_value, 'foofoo');
+
+    // Clear one of them with a wildcard and make sure the other one is still
+    // valid.
+    cache_clear_all('foobar', $this->default_bin, TRUE);
+    $this->assertCacheRemoved(t('Foobar cache invalidated.'), 'foobar');
+    $this->assertCacheExists(t('Foofoo cache still valid.'), $this->default_value, 'foofoo');
+
+    // Set and clear a cache with a different, equally short cid/wildcard.
+    cache_set('bar:1', $this->default_value, $this->default_bin);
+    $this->assertCacheExists(t('Bar cache was set.'), $this->default_value, 'bar:1');
+
+    cache_clear_all('bar', $this->default_bin, TRUE);
+    $this->assertCacheRemoved(t('Bar cache invalidated.'), 'bar:1');
+    $this->assertCacheExists(t('Foofoo cache still valid.'), $this->default_value, 'foofoo');
+
+    // Clear cache with an even shorter wildcard. This results in a full bin
+    // bin clear, all entries are marked invalid.
+    cache_set('bar:2', $this->default_value, $this->default_bin);
+    cache_clear_all('ba', $this->default_bin, TRUE);
+    $this->assertCacheRemoved(t('Bar:1 cache invalidated.'), 'bar:1');
+    $this->assertCacheRemoved(t('Bar:2 cache invalidated.'), 'bar:2');
+    $this->assertCacheRemoved(t('Foofoo cache invalidated.'), 'foofoo');
+  }
+
+
+  /**
+   * Test clearing using a cid.
+   */
+  function clearCidTest() {
+    cache_set('test_cid_clear', $this->default_value, $this->default_bin);
+
+    $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
+    cache_clear_all('test_cid_clear', $this->default_bin);
+
+    $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
+
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard false.'));
+    cache_clear_all('*', $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches still exists after clearing cid "*" with wildcard false.'));
+  }
+
+  /**
+   * Test cache clears using wildcard prefixes.
+   */
+  function clearWildcardPrefixTest() {
+    $this->resetVariables();
+    cache_set('test_cid_clear:1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear:2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear:1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear:2', $this->default_value),
+                      t('Two caches were created for checking cid substring with wildcard true.'));
+    cache_clear_all('test_cid_clear:', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear:1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear:2', $this->default_value),
+                      t('Two caches removed after clearing cid substring with wildcard true.'));
+    // Test for the case where a wildcard object disappears, for example a
+    // partial memcache restart or eviction.
+    cache_set('test_cid_clear:1', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear:1', $this->default_value), 'The cache was created successfully.');
+    cache_clear_all('test_', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear:1', $this->default_value), 'The cache was cleared successfully.');
+    // Delete the wildcard manually to simulate an eviction.
+    $wildcard = '.wildcard-' . 'test_cid_clear:';
+    dmemcache_delete($wildcard, $this->default_bin);
+    // Reset the memcache_wildcards() static cache.
+    // @todo: this is a class object in D7.
+    //memcache_wildcards(FALSE, FALSE, FALSE, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear:1', $this->default_value), 'The cache was cleared successfully.');
+  }
+
+  /**
+   * Test wildcard flushing on separate pages to ensure no static cache is used.
+   */
+  function testClearWildcardOnSeparatePages() {
+
+    $random_wildcard = $this->randomName(2) . ':' . $this->randomName(3);
+    $random_key = $random_wildcard . ':' . $this->randomName(4) . ':' . $this->randomName(2);
+    $random_value = $this->randomName();
+
+    $this->drupalGetAJAX('memcache-test/clear-cache');
+
+    $data = $this->drupalGetAJAX('memcache-test/set/' . $random_key . '/' . $random_value);
+
+    $this->assertTrue(is_array($data), 'Cache has data.');
+    $this->assertEqual($random_key, $data['cid'], 'Cache keys match.');
+    $this->assertEqual($random_value, $data['data'], 'Cache values match.');
+
+    $data = $this->drupalGetAJAX('memcache-test/get/' . $random_key);
+    $this->assertEqual($random_key, $data['cid'], 'Cache keys match.');
+    $this->assertEqual($random_value, $data['data'], 'Cache values match.');
+
+    $this->drupalGet('memcache-test/wildcard-clear/' . $random_wildcard);
+
+    $data = $this->drupalGetAJAX('memcache-test/get/' . $random_key);
+    $this->assertFalse($data, 'Cache was properly flushed.');
+
+    $data = $this->drupalGetAJAX('memcache-test/set/' . $random_key . '/' . $random_value);
+
+    $this->assertTrue(is_array($data), 'Cache has data.');
+    $this->assertEqual($random_key, $data['cid'], 'Cache keys match.');
+    $this->assertEqual($random_value, $data['data'], 'Cache values match.');
+
+    $data = $this->drupalGetAJAX('memcache-test/get/' . $random_key);
+    $this->assertEqual($random_key, $data['cid'], 'Cache keys match.');
+    $this->assertEqual($random_value, $data['data'], 'Cache values match.');
+
+    $this->drupalGet('memcache-test/wildcard-clear/' . $random_wildcard);
+
+    $data = $this->drupalGetAJAX('memcache-test/get/' . $random_key);
+    $this->assertFalse($data, 'Cache was properly flushed.');
+  }
+
+}
+
+/**
+ * Test some real world cache scenarios with default modules.
+ *
+ * Please make sure you've set the proper memcache settings in the settings.php.
+ * Looks like I've not chance to change the cache settings to what's needed by
+ * this test.
+ */
+class MemCacheRealWorldCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Real world cache tests',
+      'description' => 'Test some real world cache scenarios.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('menu');
+  }
+
+  /**
+   * Test if the menu module caching acts as expected.
+   *
+   * The menu module clears the affected menu if an menu item is changed using
+   * wildcards.
+   */
+  function testMenu() {
+    // Create and login user.
+    $account = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content'));
+    $this->drupalLogin($account);
+
+    // Add Menu Link to test with
+    $item = $this->addMenuLink();
+    $original_title = $item['link_title'];
+
+    // Check if menu link is displayed.
+    $this->drupalGet('');
+    $this->assertText($original_title, 'Menu item displayed in frontend');
+
+    // Change menu item multiple times and check if the change is reflected.
+    for($i=0; $i < 3; $i++) {
+      // Edit menu link.
+      $edit = array();
+      $edit['link_title'] = $this->randomName(16);;
+      $this->drupalPost("admin/structure/menu/item/{$item['mlid']}/edit", $edit, t('Save'));
+      if (!$this->assertResponse(200)) {
+        // One fail is enough.
+        break;
+      }
+      // Verify edited menu link.
+      if (!$this->drupalGet('admin/structure/menu/manage/' . $item['menu_name'])) {
+        // One fail is enough.
+        break;
+      }
+      $this->assertText($edit['link_title'], 'Menu link was edited');
+      $this->drupalGet('');
+      if (!$this->assertText($edit['link_title'], 'Change is reflected in frontend')) {
+        // One fail is enough.
+        break;
+      }
+    }
+  }
+
+  /**
+   * Adds a menu link.
+   *
+   * @see MenuTestCase::addMenuLink()
+   */
+  function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'main-menu') {
+    // View add menu link page.
+    $this->drupalGet("admin/structure/menu/manage/$menu_name/add");
+    $this->assertResponse(200);
+
+    $title = '!OriginalLink_' . $this->randomName(16);
+    $edit = array(
+        'link_path' => $link,
+        'link_title' => $title,
+        'description' => '',
+        'enabled' => TRUE, // Use this to disable the menu and test.
+        'expanded' => TRUE, // Setting this to true should test whether it works when we do the std_user tests.
+        'parent' =>  $menu_name . ':' . $plid,
+        'weight' => '0',
+    );
+
+    // Add menu link.
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertResponse(200);
+    // Unlike most other modules, there is no confirmation message displayed.
+    $this->assertText($title, 'Menu link was added');
+
+    $item = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => $title))->fetchAssoc();
+    return $item;
+  }
+}
diff --git a/sites/all/modules/memcache/tests/memcache6.test b/sites/all/modules/memcache/tests/memcache6.test
new file mode 100644
index 0000000000000000000000000000000000000000..197b6e9b5220117a8919ab577248a9d29dcaef66
--- /dev/null
+++ b/sites/all/modules/memcache/tests/memcache6.test
@@ -0,0 +1,343 @@
+<?php
+
+class MemcacheTestCase extends DrupalWebTestCase {
+  protected $default_bin = 'cache';
+  protected $default_cid = 'test_temporary';
+  protected $default_value = 'MemcacheTest';
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Check whether or not a cache entry exists.
+   *
+   * @param $cid
+   *   The cache id.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function checkCacheExists($cid, $var, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    $cache = cache_get($cid, $bin);
+
+    return isset($cache->data) && $cache->data == $var;
+  }
+
+  /**
+   * Assert or a cache entry exists.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+    if ($var == NULL) {
+      $var = $this->default_value;
+    }
+
+    $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
+  }
+
+  /**
+   * Assert or a cache entry has been removed.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+
+    $cache = cache_get($cid, $bin);
+    $this->assertFalse($cache, $message);
+  }
+
+  /**
+   * Perform the general wipe.
+   * @param $bin
+   *   The bin to perform the wipe on.
+   */
+  protected function generalWipe($bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    cache_clear_all(NULL, $bin);
+  }
+
+  /**
+   * Setup the lifetime settings for caching.
+   *
+   * @param $time
+   *   The time in seconds the cache should minimal live.
+   */
+  protected function setupLifetime($time) {
+    variable_set('cache_lifetime', $time);
+    variable_set('cache_flush', 0);
+  }
+}
+
+class MemCacheSavingCase extends MemcacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Memcache saving test',
+      'description' => 'Check our variables are saved and restored the right way.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    variable_set("cache_flush_cache", 0);
+  }
+
+  /**
+   * Test the saving and restoring of a string.
+   */
+  function testString() {
+    $this->checkVariable($this->randomName(100));
+  }
+
+  /**
+   * Test the saving and restoring of an integer.
+   */
+  function testInteger() {
+    $this->checkVariable(100);
+  }
+
+  /**
+   * Test the saving and restoring of a double.
+   */
+  function testDouble() {
+    $this->checkVariable(1.29);
+  }
+
+  /**
+   * Test the saving and restoring of an array.
+   */
+  function testArray() {
+    $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
+  }
+
+  /**
+   * Test the saving and restoring of an object.
+   */
+  function testObject() {
+    $test_object = new stdClass();
+    $test_object->test1 = $this->randomName(100);
+    $test_object->test2 = 100;
+    $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
+
+    cache_set('test_object', $test_object, 'cache');
+    $cache = cache_get('test_object', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
+  }
+
+  /*
+   * Check or a variable is stored and restored properly.
+   **/
+  function checkVariable($var) {
+    cache_set('test_var', $var, 'cache');
+    $cache = cache_get('test_var', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
+  }
+}
+
+/**
+ * Test cache clearing methods.
+ */
+class MemCacheClearCase extends MemcacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache clear test',
+      'description' => 'Check our clearing is done the proper way.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    $this->default_bin = 'cache_page';
+    $this->default_value = $this->randomName(10);
+
+    parent::setUp();
+  }
+
+
+  /**
+   * Test clearing the cache with a cid, no cache lifetime.
+   */
+  function testClearCidNoLifetime() {
+    $this->clearCidTest();
+  }
+
+  /**
+   * Test clearing the cache with a cid, with cache lifetime.
+   */
+  function testClearCidLifetime() {
+    variable_set('cache_lifetime', 6000);
+    $this->clearCidTest();
+  }
+
+  /**
+   * Test clearing using wildcard prefixes, no cache lifetime.
+   */
+  function testClearWildcardNoLifetime() {
+    $this->clearWildcardPrefixTest();
+  }
+
+  /**
+   * Test clearing using wildcard prefix, with cache lifetime.
+   */
+  function testClearWildcardLifetime() {
+    variable_set('cache_lifetime', 6000);
+    $this->clearWildcardPrefixTest();
+  }
+
+  /**
+   * Test full bin flushes with no cache lifetime.
+   */
+  function testClearWildcardFull() {
+    variable_set("cache_flush_$this->default_bin", 0);
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard true.'));
+    cache_clear_all('*', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid "*" with wildcard true.'));
+  }
+
+  /**
+   * Test full bin flushes with cache lifetime.
+   */
+  function testClearCacheLifetime() {
+    variable_set("cache_flush_$this->default_bin", 0);
+    variable_set('cache_lifetime', 600);
+
+    // Set a cache item with an expiry.
+    cache_set('test_cid', $this->default_value, $this->default_bin, time() + 3600);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was created successfully.');
+
+    // Set a permanent cache item.
+    cache_set('test_cid_2', $this->default_value, $this->default_bin);
+
+    // Clear the page and block caches.
+    cache_clear_all();
+    // Since the cache was cleared within the current session, cache_get()
+    // should return false.
+    $this->assertFalse($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was cleared successfully.');
+
+    // However permament items should stay in place.
+    $this->assertTrue($this->checkCacheExists('test_cid_2', $this->default_value), 'Cache item was not cleared');
+
+    // If $_SESSION['cache_flush'] is not set, then the expired item should be returned.
+    unset($_SESSION['cache_flush']);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item is still returned due to minimum cache lifetime.');
+
+    // Set a much shorter cache lifetime.
+    variable_set('cache_content_flush_' . $this->default_bin, 0);
+    variable_set('cache_lifetime', 1);
+    cache_set('test_cid_1', $this->default_value, $this->default_bin, time() + 6000);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was created successfully.');
+    sleep(2);
+    cache_clear_all();
+    $this->assertFalse($this->checkCacheExists('test_cid', $this->default_value), 'Cache item is not returned once minimum cache lifetime has expired.');
+
+    // Reset the cache clear variables.
+    variable_set('cache_content_flush_' . $this->default_bin, 0);
+    variable_set('cache_lifetime', 6000);
+    sleep(1);
+
+    // Confirm that cache_lifetime does not take effect for full bin flushes.
+    cache_set('test_cid', $this->default_value, $this->default_bin, time() + 6000);
+    $this->assertTrue($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was created successfully.');
+    cache_set('test_cid_2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_2', $this->default_value), 'Cache item was created successfully.');
+
+    // Now flush the bin.
+    cache_clear_all('*', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid', $this->default_value), 'Cache item was cleared successfully.');
+    $this->assertFalse($this->checkCacheExists('test_cid_2', $this->default_value), 'Cache item was cleared successfully.');
+  }
+
+
+  /**
+   * Test clearing using a cid.
+   */
+  function clearCidTest() {
+    variable_set("cache_flush_$this->default_bin", 0);
+    cache_set('test_cid_clear', $this->default_value, $this->default_bin);
+
+    $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
+    cache_clear_all('test_cid_clear', $this->default_bin);
+
+    $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
+
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard false.'));
+    cache_clear_all('*', $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches still exists after clearing cid "*" with wildcard false.'));
+  }
+
+  /**
+   * Test cache clears using wildcard prefixes.
+   */
+  function clearWildcardPrefixTest() {
+    variable_set("cache_flush_$this->default_bin", 0);
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid substring with wildcard true.'));
+    cache_clear_all('test_', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid substring with wildcard true.'));
+    // Test for the case where a wildcard object disappears, for example a
+    // partial memcache restart or eviction.
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value), 'The cache was created successfully.');
+    cache_clear_all('test_', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value), 'The cache was cleared successfully.');
+    // Delete the wildcard manually to simulate an eviction.
+    $wildcard = '.wildcard-' . 'test_';
+    dmemcache_delete($wildcard, $this->default_bin);
+    // Reset the memcache_wildcards() static cache.
+    memcache_wildcards(FALSE, FALSE, FALSE, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value), 'The cache was cleared successfully.');
+  }
+}
diff --git a/sites/all/modules/memcache/tests/memcache_test.info b/sites/all/modules/memcache/tests/memcache_test.info
new file mode 100644
index 0000000000000000000000000000000000000000..5669c98d8af9eea1ba53401f6feb644401c3fc75
--- /dev/null
+++ b/sites/all/modules/memcache/tests/memcache_test.info
@@ -0,0 +1,12 @@
+name = Memcache test
+description = Support module for memcache testing.
+package = Testing
+core = 7.x
+hidden = TRUE
+
+; Information added by drupal.org packaging script on 2012-01-07
+version = "7.x-1.0-rc3"
+core = "7.x"
+project = "memcache"
+datestamp = "1325917859"
+
diff --git a/sites/all/modules/memcache/tests/memcache_test.module b/sites/all/modules/memcache/tests/memcache_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..f0f3956bb32a2a0306a73dab8788ada80f55847f
--- /dev/null
+++ b/sites/all/modules/memcache/tests/memcache_test.module
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * Implements hook_menu().
+ */
+function memcache_test_menu() {
+  $items['memcache-test/lock-acquire'] = array(
+    'title' => 'Lock acquire',
+    'page callback' => 'memcache_test_lock_acquire',
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['memcache-test/lock-exit'] = array(
+    'title' => 'Lock acquire then exit',
+    'page callback' => 'memcache_test_lock_exit',
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['memcache-test/set/%/%'] = array(
+    'title' => 'Set a value with a key',
+    'page callback' => 'memcache_test_set',
+    'page arguments' => array(2, 3),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['memcache-test/get/%'] = array(
+    'title' => 'Get a value from the cache',
+    'page callback' => 'memcache_test_get',
+    'page arguments' => array(2),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['memcache-test/wildcard-clear/%'] = array(
+    'title' => 'Clear the cache with a wildcard',
+    'page callback' => 'memcache_test_wildcard_flush',
+    'page arguments' => array(2),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['memcache-test/clear/%'] = array(
+    'title' => 'Clear the cache with a wildcard',
+    'page callback' => 'memcache_test_clear',
+    'page arguments' => array(2),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['memcache-test/clear-cache'] = array(
+    'title' => 'Clear the cache with a wildcard',
+    'page callback' => 'memcache_test_clear_cache',
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  return $items;
+}
+
+/**
+ * Try to acquire a named lock and report the outcome.
+ */
+function memcache_test_lock_acquire() {
+  if (lock_acquire('memcache_test_lock_acquire')) {
+    lock_release('memcache_test_lock_acquire');
+    return 'TRUE: Lock successfully acquired in memcache_test_lock_acquire()';
+  }
+  else {
+    return 'FALSE: Lock not acquired in memcache_test_lock_acquire()';
+  }
+}
+
+/**
+ * Try to acquire a specific lock, and then exit.
+ */
+function memcache_test_lock_exit() {
+  if (lock_acquire('memcache_test_lock_exit', 900)) {
+    echo 'TRUE: Lock successfully acquired in memcache_test_lock_exit()';
+    // The shut-down function should release the lock.
+    exit();
+  }
+  else {
+    return 'FALSE: Lock not acquired in memcache_test_lock_exit()';
+  }
+}
+
+/**
+ * Set a value into the cache.
+ */
+function memcache_test_set($key, $value) {
+  $cache = cache_set($key, $value, 'memcache');
+  drupal_json_output(cache_get($key, 'memcache'));
+}
+
+/**
+ * Set a value into the cache.
+ */
+function memcache_test_get($key) {
+  $GLOBALS['in_test'] = TRUE;
+  drupal_json_output(cache_get($key, 'memcache'));
+}
+
+/**
+ * Clear cache using a wildcard.
+ */
+function memcache_test_wildcard_flush($key) {
+  cache_clear_all($key, 'memcache', TRUE);
+  drupal_json_output($key);
+}
+
+/**
+ * Clear cache using a specific key.
+ */
+function memcache_test_clear($key) {
+  cache_clear_all($key, 'memcache');
+
+  drupal_json_output($key);
+}
+
+/**
+* Clear complete cache.
+*/
+function memcache_test_clear_cache() {
+  cache_clear_all(NULL, 'memcache');
+  drupal_json_output();
+}
diff --git a/sites/all/modules/memcache/memcache-session.inc b/sites/all/modules/memcache/unstable/memcache-session.inc
similarity index 99%
rename from sites/all/modules/memcache/memcache-session.inc
rename to sites/all/modules/memcache/unstable/memcache-session.inc
index a0137aeec9a40805b4154bfc80e7379cebaf4d6e..ee56e57c3f66dc199df39ca5cda84484fc9f0b15 100644
--- a/sites/all/modules/memcache/memcache-session.inc
+++ b/sites/all/modules/memcache/unstable/memcache-session.inc
@@ -1,14 +1,17 @@
 <?php
 
-require_once dirname(__FILE__) . '/dmemcache.inc';
-
 /**
  * @file
  * User session handling functions.
  *
+ * Note session support is not fully ported to Drupal 7 and is not recommended
+ * for production sites.
+ *
  * An alternative to includes/session.inc.
  */
 
+require_once dirname(__FILE__) . '/dmemcache.inc';
+
 /**
  * Implement hook_user() using a required module's namespace since memcache is 
  * not a module and thus can't implement hooks directly.