This is patch40 to PennMUSH 1.7.7. After applying this patch, you will have version 1.7.7p40 To apply this patch, save it to a file in your top-level MUSH directory, and do the following: patch -p1 < 1.7.7-patch40 make clean make install *** ADJUST chunk_migrate IN mush.cnf. SEE BELOW! *** If you use GNU patch 2.2, you probably want the above to be 'patch -b -p1', not just 'patch -p1'. Unix (or cygwin) users need not worry about failed hunks in src/switchinc.c, hdrs/switches.h, hdrs/cmds.h, or hdrs/funs.h. These files are automatically rebuilt on compile. On the off chance they appear not to be, simply rm them and re-run make. Then @shutdown and restart your MUSH. - Alan/Javelin In this patch: Major Changes: * Another pass at the chunk allocator! Simpler but effective. Folks should, however, greatly reduce their chunk_migrate value in mush.cnf -- we suggest '50'. [TAP] Commands: * @command/alias can alias commands. Patch by Walker@M*U*S*H. Functions: * zwho() and zmwho(). [EEH] Minor Changes: * utils/penn-install is no longer part of the PennMUSH distribution (it's part of the Debian maintainer's stuff). [EEH] * Inheritable @locks are inherited off of ancestor objects. Suggested by Zith@Lovarii. [SW] Fixes: * Infinite recursion in @lock/examine fixed. Report by Amy Kou'ai (Amy@ShoujoAi) and Sparta Kerleon (Sparta@ShoujoAi). [TAP] * @flag/letter now allows setting a flag's letter to one used by another flag that works on different object types. Report by Lenon. * Translation corrections by Cheetah@M*U*S*H and [EEH]. * Added __USE_POSIX to the cflags for linux, and removed checking for -lbind, to help SuSE 9. Report by Ambrosia@M*U*S*H. * Fixes to panic db loading logic. [SW] * escape() shouldn't double-escape the first character of the string when it's a special character. Report by Walker@M*U*S*H. Prereq: 1.7.7p39 *** 1_7_7.1228/Patchlevel Mon, 25 Oct 2004 17:41:11 -0500 dunemush (pennmush/5_Patchlevel 1.17.1.11.1.41 600) --- 1_7_7.1248(w)/Patchlevel Wed, 01 Dec 2004 10:36:52 -0600 dunemush (pennmush/5_Patchlevel 1.17.1.11.1.42 600) *************** *** 1,2 **** Do not edit this file. It is maintained by the official PennMUSH patches. ! This is PennMUSH 1.7.7p39 --- 1,2 ---- Do not edit this file. It is maintained by the official PennMUSH patches. ! This is PennMUSH 1.7.7p40 *** 1_7_7.1228/CHANGES.177 Mon, 25 Oct 2004 17:41:11 -0500 dunemush (pennmush/g/23_CHANGES 1.48.1.258.1.14.1.212 600) --- 1_7_7.1248(w)/CHANGES.177 Fri, 03 Dec 2004 17:49:53 -0600 dunemush (pennmush/g/23_CHANGES 1.48.1.258.1.14.1.224 600) *************** *** 18,23 **** --- 18,51 ---- ========================================================================== + Version 1.7.7 patchlevel 40 December 1, 2004 + + Major Changes: + * Another pass at the chunk allocator! Simpler but effective. + Folks should, however, greatly reduce their chunk_migrate + value in mush.cnf -- we suggest '50'. [TAP] + Commands: + * @command/alias can alias commands. Patch by Walker@M*U*S*H. + Functions: + * zwho() and zmwho(). [EEH] + Minor Changes: + * utils/penn-install is no longer part of the PennMUSH distribution + (it's part of the Debian maintainer's stuff). [EEH] + * Inheritable @locks are inherited off of ancestor objects. + Suggested by Zith@Lovarii. [SW] + Fixes: + * Infinite recursion in @lock/examine fixed. Report by + Amy Kou'ai (Amy@ShoujoAi) and Sparta Kerleon (Sparta@ShoujoAi). [TAP] + * @flag/letter now allows setting a flag's letter to one used by + another flag that works on different object types. Report by Lenon. + * Translation corrections by Cheetah@M*U*S*H and [EEH]. + * Added __USE_POSIX to the cflags for linux, and removed + checking for -lbind, to help SuSE 9. Report by Ambrosia@M*U*S*H. + * Fixes to panic db loading logic. [SW] + * escape() shouldn't double-escape the first character of the + string when it's a special character. Report by Walker@M*U*S*H. + + Version 1.7.7 patchlevel 39 October 25, 2004 Major Changes: *** 1_7_7.1228/game/txt/hlp/penntop.hlp Sat, 09 Oct 2004 15:31:47 -0500 dunemush (pennmush/13_penntop.hl 1.2.1.27.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.12.1.1.1.1.1.8.1.11.1.1.1.2 600) --- 1_7_7.1248(w)/game/txt/hlp/penntop.hlp Wed, 01 Dec 2004 20:21:34 -0600 dunemush (pennmush/13_penntop.hl 1.2.1.27.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.12.1.1.1.1.1.8.1.11.1.1.1.3 600) *************** *** 391,399 **** ----------------------------------------------------------------------- UNIX Tinyfugue tf.tcp.com /pub/tinyfugue ! WINDOWS 32-bit MUSHClient ftp.pennmush.org ! /pub/PennMUSH/Win32Binaries SimpleMU http://simplemu.onlineroleplay.com MACINTOSH MUDDweller http://www.shareware.com (search for Muddweller) & CONTROL --- 391,399 ---- ----------------------------------------------------------------------- UNIX Tinyfugue tf.tcp.com /pub/tinyfugue ! WINDOWS 32-bit MUSHClient http://www.mushclient.com SimpleMU http://simplemu.onlineroleplay.com + MuckClient http://www.xcalibur.co.uk/MuckClient/ MACINTOSH MUDDweller http://www.shareware.com (search for Muddweller) & CONTROL *** 1_7_7.1228/game/txt/hlp/pennfunc.hlp Mon, 25 Oct 2004 17:35:54 -0500 dunemush (pennmush/16_pennfunc.h 1.2.1.50.1.1.1.1.1.2.1.7.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.23 600) --- 1_7_7.1248(w)/game/txt/hlp/pennfunc.hlp Fri, 03 Dec 2004 17:49:19 -0600 dunemush (pennmush/16_pennfunc.h 1.2.1.50.1.1.1.1.1.2.1.7.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.24 600) *************** *** 113,120 **** cmds() conn() doing() height() hostname() hidden() idle() ipaddr() lports() lwho() ! mwho() ports() pueblo() recv() sent() ! ssl() terminfo() width() & Dbref functions Dbref functions return a dbref or list of dbrefs related to some value on an object. --- 113,121 ---- cmds() conn() doing() height() hostname() hidden() idle() ipaddr() lports() lwho() ! mwho() nmwho() nwho() ports() pueblo() ! recv() sent() ssl() terminfo() width() ! xmwho() xwho() zmwho() zwho() & Dbref functions Dbref functions return a dbref or list of dbrefs related to some value on an object. *************** *** 4531,4536 **** --- 4532,4560 ---- players. See also: lwho(), mwho(), nwho() + + & ZMWHO() + zmwho() + + This returns a list of the dbref numbers for all current-connected, + non-hidden players within a location belonging to the specified zone. + It's exactly the same as zwho() used by a mortal, and is suitable for + use on privileged global objects who need an unprivileged zwho-list. + + See also: zwho() + + & ZWHO() + zwho([,]) + + This returns a list of the dbref numbers for all currently-connected + players within a location belonging to the specified zone. When mortals + use this function, the dbref numbers of DARK wizards or hidden royalty + do NOT appear on the dbref list. + + If is given by a privileged user, zwho() returns a dbref list + using 's privileges. + + See also: zmwho() & ZEMIT() & NSZEMIT() zemit(, ) *** 1_7_7.1228/game/txt/hlp/penncmd.hlp Sat, 09 Oct 2004 15:42:59 -0500 dunemush (pennmush/18_penncmd.hl 1.2.1.1.1.47.1.1.1.1.1.3.1.4.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.10.1.1.1.1.1.1.1.1.1.1.1.45 600) --- 1_7_7.1248(w)/game/txt/hlp/penncmd.hlp Wed, 01 Dec 2004 10:01:47 -0600 dunemush (pennmush/18_penncmd.hl 1.2.1.1.1.47.1.1.1.1.1.3.1.4.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.10.1.1.1.1.1.1.1.1.1.1.1.46 600) *************** *** 570,575 **** --- 570,576 ---- & @command @command @command/ + @command/alias = @command provides information about and controls the availability of other commands. *************** *** 582,590 **** /off : Synonym for /disable /enable : Enable the /on : Synonym for /enable /quiet : Don't make noisy output when doing one of the above /add : Creates a useless command. @hook/override it. ! /delete : Deletes a command added by @command/add See HELP RESTRICT for more. (continued in help @command2) --- 583,592 ---- /off : Synonym for /disable /enable : Enable the /on : Synonym for /enable + /alias : Creates an alias for a command. /quiet : Don't make noisy output when doing one of the above /add : Creates a useless command. @hook/override it. ! /delete : Deletes a command added by @command/add, or aliases. See HELP RESTRICT for more. (continued in help @command2) *** 1_7_7.1228/game/mushcnf.dst Sat, 09 Oct 2004 15:15:15 -0500 dunemush (pennmush/41_mushcnf.ds 1.1.1.19.1.1.1.2.1.1.1.8.1.1.1.1.1.34 600) --- 1_7_7.1248(w)/game/mushcnf.dst Tue, 23 Nov 2004 14:15:18 -0600 dunemush (pennmush/41_mushcnf.ds 1.1.1.19.1.1.1.2.1.1.1.8.1.1.1.1.1.34.1.1 600) *************** *** 152,158 **** # The number of attributes that may be moved at one time, once per # second. The higher the value, the faster memory gets defragmented, # but at a greater CPU cost. ! chunk_migrate 500 ### ### SSL support --- 152,158 ---- # The number of attributes that may be moved at one time, once per # second. The higher the value, the faster memory gets defragmented, # but at a greater CPU cost. ! chunk_migrate 50 ### ### SSL support *** 1_7_7.1228/hints/linux_2.sh Tue, 22 Aug 2000 12:58:00 -0500 dunemush (pennmush/b/7_linux_2.sh 1.6 600) --- 1_7_7.1248(w)/hints/linux_2.sh Mon, 08 Nov 2004 10:45:46 -0600 dunemush (pennmush/b/7_linux_2.sh 1.7 600) *************** *** 1,2 **** --- 1,4 ---- nm_opt='-B' + ccflags='-D__USE_POSIX' + libswanted='nsl m c crypt intl resolv' echo "I suggest using sysmalloc when you edit options.h." *** 1_7_7.1228/src/lock.c Sat, 17 Jul 2004 20:24:38 -0500 dunemush (pennmush/c/6_lock.c 1.17.1.13.1.1.1.1.1.22 660) --- 1_7_7.1248(w)/src/lock.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/6_lock.c 1.17.1.13.1.1.1.1.1.23 660) *************** *** 324,346 **** getlockstruct(dbref thing, lock_type type) { lock_list *ll; ! dbref p; ! int cmp, count; ! count = 0; ! for (p = thing; GoodObject(p); p = Parent(p)) { ! if (count++ > 100) ! return NULL; ! ll = Locks(p); ! while (ll && L_TYPE(ll)) { ! cmp = strcasecmp(L_TYPE(ll), type); ! if (cmp == 0) ! return (p != thing && (ll->flags & LF_PRIVATE)) ? NULL : ll; ! else if (cmp > 0) ! break; ! ll = ll->next; } ! } return NULL; } --- 324,352 ---- getlockstruct(dbref thing, lock_type type) { lock_list *ll; ! dbref p = thing, ancestor = NOTHING; ! int cmp, count = 0, ancestor_in_chain = 0; ! if (GoodObject(thing)) ! ancestor = Ancestor_Parent(thing); ! do { ! for (; GoodObject(p); p = Parent(p)) { ! if (count++ > 100) ! return NULL; ! if (p == ancestor) ! ancestor_in_chain = 1; ! ll = Locks(p); ! while (ll && L_TYPE(ll)) { ! cmp = strcasecmp(L_TYPE(ll), type); ! if (cmp == 0) ! return (p != thing && (ll->flags & LF_PRIVATE)) ? NULL : ll; ! else if (cmp > 0) ! break; ! ll = ll->next; ! } } ! p = ancestor; ! } while (!ancestor_in_chain && !Orphan(thing) && GoodObject(ancestor)); return NULL; } *** 1_7_7.1228/src/game.c Thu, 02 Sep 2004 11:04:56 -0500 dunemush (pennmush/c/10_game.c 1.50.1.8.1.1.1.1.2.1.1.1.2.1.1.4.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.5.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.12.1.3.1.7.1.2.2.1.1.32.1.1 660) --- 1_7_7.1248(w)/src/game.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/10_game.c 1.50.1.8.1.1.1.1.2.1.1.1.2.1.1.4.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.5.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.12.1.3.1.7.1.2.2.1.1.32.1.2 660) *************** *** 854,860 **** */ panicdb = ((globals.indb_flags & DBF_PANIC) && !feof(f)); - /* everything ok */ if (!panicdb) db_close(f); --- 854,859 ---- *************** *** 875,880 **** --- 874,881 ---- do_rawlog(LT_ERR, T("WARNING: God (#%d) is NOT a player."), GOD); /* read mail database */ + mail_init(); + if (panicdb) { do_rawlog(LT_ERR, T("LOADING: Trying to get mail from %s"), infile); if (load_mail(f) <= 0) { *************** *** 882,899 **** db_close(f); panicdb = 0; } ! } else ! f = db_open(mailfile); ! /* okay, read it in */ ! if (f == NULL) { ! mail_init(); ! } else { ! do_rawlog(LT_ERR, "LOADING: %s", mailfile); ! dbline = 0; ! load_mail(f); ! do_rawlog(LT_ERR, "LOADING: %s (done)", mailfile); ! db_close(f); } init_chatdb(); --- 883,900 ---- db_close(f); panicdb = 0; } ! } ! if (!panicdb) { ! f = db_open(mailfile); ! /* okay, read it in */ ! if (f) { ! do_rawlog(LT_ERR, "LOADING: %s", mailfile); ! dbline = 0; ! load_mail(f); ! do_rawlog(LT_ERR, "LOADING: %s (done)", mailfile); ! db_close(f); ! } } init_chatdb(); *************** *** 905,922 **** db_close(f); panicdb = 0; } ! } else ! f = db_open(options.chatdb); ! if (f) { ! do_rawlog(LT_ERR, "LOADING: %s", options.chatdb); ! dbline = 0; ! if (load_chatdb(f)) { ! do_rawlog(LT_ERR, "LOADING: %s (done)", options.chatdb); ! } else { ! do_rawlog(LT_ERR, "ERROR LOADING %s", options.chatdb); ! db_close(f); ! return -1; } } --- 906,925 ---- db_close(f); panicdb = 0; } ! } ! if (!panicdb) { ! f = db_open(options.chatdb); ! if (f) { ! do_rawlog(LT_ERR, "LOADING: %s", options.chatdb); ! dbline = 0; ! if (load_chatdb(f)) { ! do_rawlog(LT_ERR, "LOADING: %s (done)", options.chatdb); ! } else { ! do_rawlog(LT_ERR, "ERROR LOADING %s", options.chatdb); ! db_close(f); ! return -1; ! } } } *** 1_7_7.1228/src/funstr.c Thu, 02 Sep 2004 11:04:56 -0500 dunemush (pennmush/c/13_funstr.c 1.28.1.1.1.2.1.4.1.6.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.3.1.5.1.3.1.2.1.1.1.1.1.1.1.1.1.14.1.23 660) --- 1_7_7.1248(w)/src/funstr.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/13_funstr.c 1.28.1.1.1.2.1.4.1.6.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.3.1.5.1.3.1.2.1.1.1.1.1.1.1.1.1.14.1.25 660) *************** *** 1085,1091 **** if (arglens[0]) { safe_chr('\\', buff, bp); for (s = (unsigned char *) args[0]; *s; s++) { ! if (escaped_chars[*s]) safe_chr('\\', buff, bp); safe_chr(*s, buff, bp); } --- 1085,1091 ---- if (arglens[0]) { safe_chr('\\', buff, bp); for (s = (unsigned char *) args[0]; *s; s++) { ! if ((s != (unsigned char *) args[0]) && escaped_chars[*s]) safe_chr('\\', buff, bp); safe_chr(*s, buff, bp); } *** 1_7_7.1228/src/function.c Sat, 09 Oct 2004 15:25:31 -0500 dunemush (pennmush/c/18_function.c 1.29.1.14.1.3.1.6.1.1.1.1.1.14.1.2.1.1.1.7.1.22.1.11.1.3.1.24.1.4.1.1 660) --- 1_7_7.1248(w)/src/function.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/18_function.c 1.29.1.14.1.3.1.6.1.1.1.1.1.14.1.2.1.1.1.7.1.22.1.11.1.3.1.24.1.4.1.2 660) *************** *** 559,564 **** --- 559,566 ---- {"ZEMIT", fun_zemit, 2, -2, FN_REG}, {"ZFUN", fun_zfun, 1, 11, FN_REG}, {"ZONE", fun_zone, 1, 2, FN_REG}, + {"ZMWHO", fun_zwho, 1, 1, FN_REG}, + {"ZWHO", fun_zwho, 1, 2, FN_REG}, {"VADD", fun_vadd, 2, 3, FN_REG}, {"VCROSS", fun_vcross, 2, 3, FN_REG}, {"VSUB", fun_vsub, 2, 3, FN_REG}, *** 1_7_7.1228/src/flags.c Thu, 02 Sep 2004 11:04:56 -0500 dunemush (pennmush/c/20_flags.c 1.1.1.1.1.1.1.1.1.1.1.1.1.6.1.2.1.1.1.1.1.2.2.2.2.1.2.1.1.3.1.2.1.1.1.1.1.1.1.1.1.3.1.9.1.2.2.1.1.2.1.56.1.12.1.1.1.29.1.2 660) --- 1_7_7.1248(w)/src/flags.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/20_flags.c 1.1.1.1.1.1.1.1.1.1.1.1.1.6.1.2.1.1.1.1.1.2.2.2.2.1.2.1.1.3.1.2.1.1.1.1.1.1.1.1.1.3.1.9.1.2.2.1.1.2.1.56.1.12.1.1.1.29.1.2.1.2 660) *************** *** 2249,2255 **** notify(player, T("Unknown failure adding alias.")); } ! /** Change a flag's alias. * \param ns name of the flagspace to use. * \param player the enactor. * \param name name of the flag. --- 2249,2255 ---- notify(player, T("Unknown failure adding alias.")); } ! /** Change a flag's letter. * \param ns name of the flagspace to use. * \param player the enactor. * \param name name of the flag. *************** *** 2281,2287 **** return; } ! if ((other = letter_to_flagptr(n, *letter, NOTYPE))) { notify_format(player, T("Letter conflicts with the %s %s."), other->name, strlower(ns)); return; --- 2281,2287 ---- return; } ! if ((other = letter_to_flagptr(n, *letter, f->type))) { notify_format(player, T("Letter conflicts with the %s %s."), other->name, strlower(ns)); return; *** 1_7_7.1228/src/extmail.c Thu, 02 Sep 2004 11:04:56 -0500 dunemush (pennmush/c/22_extmail.c 1.44.1.7.1.5.1.9.1.1.1.1.1.28 660) --- 1_7_7.1248(w)/src/extmail.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/22_extmail.c 1.44.1.7.1.5.1.9.1.1.1.1.1.29 660) *************** *** 1974,1981 **** char sbuf[BUFFER_LEN]; struct tm ttm; - mail_init(); - /* find out how many messages we should be loading */ fgets(nbuf1, sizeof(nbuf1), fp); /* If it starts with +, it's telling us the mail db flags */ --- 1974,1979 ---- *** 1_7_7.1228/src/conf.c Thu, 02 Sep 2004 11:04:56 -0500 dunemush (pennmush/c/31_conf.c 1.41.2.3.1.3.1.2.1.15.1.1.1.1.1.1.1.41 660) --- 1_7_7.1248(w)/src/conf.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/31_conf.c 1.41.2.3.1.3.1.2.1.15.1.1.1.1.1.1.1.41.1.1 660) *************** *** 1164,1170 **** options.call_lim = 10000; strcpy(options.chunk_swap_file, "data/chunkswap"); options.chunk_cache_memory = 1000000; ! options.chunk_migrate_amount = 500; options.read_remote_desc = 0; #ifdef HAS_OPENSSL strcpy(options.ssl_private_key_file, ""); --- 1164,1170 ---- options.call_lim = 10000; strcpy(options.chunk_swap_file, "data/chunkswap"); options.chunk_cache_memory = 1000000; ! options.chunk_migrate_amount = 50; options.read_remote_desc = 0; #ifdef HAS_OPENSSL strcpy(options.ssl_private_key_file, ""); *** 1_7_7.1228/src/command.c Thu, 14 Oct 2004 14:50:35 -0500 dunemush (pennmush/c/36_command.c 1.56.1.1.1.1.1.1.1.2.1.1.1.1.1.5.1.2.1.1.1.1.1.2.1.3.1.10.1.1.3.74.1.1 660) --- 1_7_7.1248(w)/src/command.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/36_command.c 1.56.1.1.1.1.1.1.1.2.1.1.1.1.1.5.1.2.1.1.1.1.1.2.1.3.1.10.1.1.3.74.1.4 660) *************** *** 58,64 **** COMLIST commands[] = { {"@COMMAND", ! "ADD DELETE EQSPLIT LSARGS RSARGS NOEVAL ON OFF QUIET ENABLE DISABLE RESTRICT", cmd_command, CMD_T_PLAYER | CMD_T_EQSPLIT, 0, 0}, {"@@", NULL, cmd_null, CMD_T_ANY | CMD_T_NOPARSE, 0, 0}, --- 58,64 ---- COMLIST commands[] = { {"@COMMAND", ! "ADD ALIAS DELETE EQSPLIT LSARGS RSARGS NOEVAL ON OFF QUIET ENABLE DISABLE RESTRICT", cmd_command, CMD_T_PLAYER | CMD_T_EQSPLIT, 0, 0}, {"@@", NULL, cmd_null, CMD_T_ANY | CMD_T_NOPARSE, 0, 0}, *************** *** 1362,1370 **** upcasestr(name); command = command_find(name); if (!command) { ! if ((*name == 0) || strchr(name, ' ') || !ok_name(name) ! || strchr(name, '\t') || strchr(name, '\n') ! || strchr(name, '\r')) { notify(player, T("Bad command name.")); } else { command_add(mush_strdup(name, "command_add"), --- 1362,1368 ---- upcasestr(name); command = command_find(name); if (!command) { ! if (!ok_command_name(name)) { notify(player, T("Bad command name.")); } else { command_add(mush_strdup(name, "command_add"), *************** *** 1389,1411 **** void do_command_delete(dbref player, char *name) { COMMAND_INFO *command; if (!God(player)) { notify(player, T("Permission denied.")); return; } ! command = command_find(name); if (!command) { notify(player, T("No such command.")); return; } ! if (command->func != cmd_unimplemented) { ! notify(player, ! T("You can't delete built-in commands. @hook/override instead.")); ! return; } - notify_format(player, T("Removed %s from command table."), command->name); - ptab_delete(&ptab_command, name); } /** Definition of the \@command command. --- 1387,1438 ---- void do_command_delete(dbref player, char *name) { + int acount; + char alias[BUFFER_LEN]; + COMMAND_INFO *cptr; COMMAND_INFO *command; + if (!God(player)) { notify(player, T("Permission denied.")); return; } ! upcasestr(name); ! command = command_find_exact(name); if (!command) { notify(player, T("No such command.")); return; } ! if (strcasecmp(command->name, name) == 0) { ! /* This is the command, not an alias */ ! if (command->func != cmd_unimplemented) { ! notify(player, ! T ! ("You can't delete built-in commands. @command/disable instead.")); ! return; ! } else { ! acount = 0; ! cptr = ptab_firstentry_new(&ptab_command, alias); ! while (cptr) { ! if (cptr == command) { ! ptab_delete(&ptab_command, alias); ! acount++; ! cptr = ptab_firstentry_new(&ptab_command, alias); ! } else ! cptr = ptab_nextentry_new(&ptab_command, alias); ! } ! mush_free((Malloc_t) command->name, "command_add"); ! mush_free((Malloc_t) command, "command"); ! if (acount > 1) ! notify_format(player, T("Removed %s and aliases from command table."), ! name); ! else ! notify_format(player, T("Removed %s from command table."), name); ! } ! } else { ! /* This is an alias. Just remove it */ ! ptab_delete(&ptab_command, name); ! notify_format(player, T("Removed %s from command table."), name); } } /** Definition of the \@command command. *************** *** 1432,1437 **** --- 1459,1480 ---- do_command_add(player, arg_left, flags); return; } + if (SW_ISSET(sw, SWITCH_ALIAS)) { + if (Wizard(player)) { + if (!ok_command_name(upcasestr(arg_right))) { + notify(player, "I can't alias a command to that!"); + } else if (!alias_command(arg_left, arg_right)) { + notify(player, "Unable to set alias."); + } else { + if (!SW_ISSET(sw, SWITCH_QUIET)) + notify(player, "Alias set."); + } + } else { + notify(player, T("Permission denied.")); + } + return; + } + if (SW_ISSET(sw, SWITCH_DELETE)) { do_command_delete(player, arg_left); return; *** 1_7_7.1228/src/bsd.c Sat, 09 Oct 2004 15:42:59 -0500 dunemush (pennmush/c/38_bsd.c 1.58.1.11.1.2.1.5.1.7.1.14.1.13.1.9.1.4.1.2.1.12.1.1.1.1.1.2.1.1.1.13.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.8.2.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.48.1.1.1.14 660) --- 1_7_7.1248(w)/src/bsd.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/c/38_bsd.c 1.58.1.11.1.2.1.5.1.7.1.14.1.13.1.9.1.4.1.2.1.12.1.1.1.1.1.2.1.1.1.13.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.8.2.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.48.1.1.1.15 660) *************** *** 4276,4281 **** --- 4276,4333 ---- return hostname; } + /* ZWHO() function - really belongs in eval.c but needs stuff declared here */ + /* ARGSUSED */ + FUNCTION(fun_zwho) + { + DESC *d; + dbref zone, victim; + int first; + int powered = (strcmp(called_as, "ZMWHO") && Priv_Who(executor)); + first = 1; + + zone = match_thing(executor, args[0]); + + if (nargs == 1) { + victim = executor; + } else if ((nargs == 2) && powered) { + if ((victim = match_thing(executor, args[1])) == 0) { + safe_str(T(e_match), buff, bp); + return; + } + } else { + safe_str(T(e_perm), buff, bp); + return; + } + + if (!GoodObject(zone) || !eval_lock(victim, zone, Zone_Lock)) { + safe_str(T(e_perm), buff, bp); + return; + } + if ((getlock(zone, Zone_Lock) == TRUE_BOOLEXP) || + (IsPlayer(zone) && !(has_flag_by_name(zone, "SHARED", TYPE_PLAYER)))) { + safe_str(T("#-1 INVALID ZONE."), buff, bp); + return; + } + + /* Use lowest privilege for victim */ + if (!Priv_Who(victim)) + powered = 0; + + DESC_ITER_CONN(d) { + if (!Hidden(d) || powered) { + if (Zone(Location(d->player)) == zone) { + if (first) { + first = 0; + } else { + safe_chr(' ', buff, bp); + } + safe_dbref(d->player, buff, bp); + } + } + } + } + /* ARGSUSED */ FUNCTION(fun_doing) { *** 1_7_7.1228/src/attrib.c Mon, 25 Oct 2004 17:13:01 -0500 dunemush (pennmush/c/40_attrib.c 1.15.1.2.1.5.1.1.1.3.1.3.1.2.1.2.1.2.2.1.1.2.1.2.1.2.1.1.1.3.1.1.1.1.1.1.3.43.1.14 660) --- 1_7_7.1248(w)/src/attrib.c Sun, 05 Dec 2004 22:51:12 -0600 dunemush (pennmush/c/40_attrib.c 1.15.1.2.1.5.1.1.1.3.1.3.1.2.1.2.1.2.2.1.1.2.1.2.1.2.1.1.1.3.1.1.1.1.1.1.3.43.1.15 660) *************** *** 244,251 **** { static char name[ATTRIBUTE_NAME_LIMIT + 1]; char *p; ! int control; ! int exam; int canlook; dbref target; dbref ancestor; --- 244,250 ---- { static char name[ATTRIBUTE_NAME_LIMIT + 1]; char *p; ! int cansee; int canlook; dbref target; dbref ancestor; *************** *** 253,273 **** int parent_depth; visible = (player == NOTHING); if (visible) { ! control = 0; ! exam = (Visual(obj) && ! eval_lock(PLAYER_START, obj, Examine_Lock) && ! eval_lock(MASTER_ROOM, obj, Examine_Lock)); canlook = 0; } else { ! control = controls(player, obj); ! exam = (Visual(obj) && eval_lock(player, obj, Examine_Lock)); canlook = can_look_at(player, obj); } /* Take an easy out if there is one... */ /* If we can't see the attribute itself, then that's easy. */ if (AF_Internal(atr) || AF_Mdark(atr) || ! !(control || exam || (AF_Visual(atr) && (!AF_Nearby(atr) || canlook)) || (!visible && !Mistrust(player) && (Owner(AL_CREATOR(atr)) == Owner(player))))) --- 252,271 ---- int parent_depth; visible = (player == NOTHING); if (visible) { ! cansee = (Visual(obj) && ! eval_lock(PLAYER_START, obj, Examine_Lock) && ! eval_lock(MASTER_ROOM, obj, Examine_Lock)); canlook = 0; } else { ! cansee = controls(player, obj) || ! (Visual(obj) && eval_lock(player, obj, Examine_Lock)); canlook = can_look_at(player, obj); } /* Take an easy out if there is one... */ /* If we can't see the attribute itself, then that's easy. */ if (AF_Internal(atr) || AF_Mdark(atr) || ! !(cansee || (AF_Visual(atr) && (!AF_Nearby(atr) || canlook)) || (!visible && !Mistrust(player) && (Owner(AL_CREATOR(atr)) == Owner(player))))) *************** *** 296,302 **** goto continue_target; } if (AF_Internal(atr) || AF_Mdark(atr) || ! !(control || exam || (AF_Visual(atr) && (!AF_Nearby(atr) || canlook)) || (!visible && !Mistrust(player) && (Owner(AL_CREATOR(atr)) == Owner(player))))) --- 294,300 ---- goto continue_target; } if (AF_Internal(atr) || AF_Mdark(atr) || ! !(cansee || (AF_Visual(atr) && (!AF_Nearby(atr) || canlook)) || (!visible && !Mistrust(player) && (Owner(AL_CREATOR(atr)) == Owner(player))))) *** 1_7_7.1228/hdrs/version.h Mon, 25 Oct 2004 17:41:11 -0500 dunemush (pennmush/c/47_version.h 1.32.1.2.1.7.1.9.1.1.1.17.1.43 660) --- 1_7_7.1248(w)/hdrs/version.h Sun, 05 Dec 2004 22:51:14 -0600 dunemush (pennmush/c/47_version.h 1.32.1.2.1.7.1.9.1.1.1.17.1.44 660) *************** *** 1,4 **** #define VERSION "1.7.7" ! #define PATCHLEVEL "39" ! #define PATCHDATE "[10/25/2004]" ! #define NUMVERSION 001007007039 --- 1,4 ---- #define VERSION "1.7.7" ! #define PATCHLEVEL "40" ! #define PATCHDATE "[12/01/2004]" ! #define NUMVERSION 001007007040 *** 1_7_7.1228/hdrs/command.h Sat, 09 Oct 2004 15:42:59 -0500 dunemush (pennmush/d/24_command.h 1.35 660) --- 1_7_7.1248(w)/hdrs/command.h Sun, 05 Dec 2004 22:51:14 -0600 dunemush (pennmush/d/24_command.h 1.36 660) *************** *** 106,123 **** char *arg_left, char *args_left[MAX_ARG], \ char *arg_right, char *args_right[MAX_ARG]) - /* For things that DON'T get auto-aliased by mkalias, like 'i' for 'inv' and 'l' for 'look' */ - - typedef struct command_alias COMALIAS; - /** An alias to a command. - * This structure represents a command alias, which consists of the command's - * real (canonical) name and an alias for that name. - */ - struct command_alias { - const char *name; /**< Canonical command name */ - const char *alias; /**< Command alias */ - }; - typedef struct command_info COMMAND_INFO; typedef void (*command_func) (COMMAND_INFO *, dbref, dbref, switch_mask, char *, char *, char *, char *, char *[MAX_ARG], char *, --- 106,111 ---- *** 1_7_7.1228/Makefile.SH Mon, 21 Jun 2004 14:59:00 -0500 dunemush (pennmush/d/30_Makefile.S 1.14.1.1.1.7.1.1.1.20 700) --- 1_7_7.1248(w)/Makefile.SH Thu, 04 Nov 2004 13:57:33 -0600 dunemush (pennmush/d/30_Makefile.S 1.14.1.1.1.7.1.1.1.21 700) *************** *** 66,75 **** # Where to install with 'make globalinstall' GLOBAL_INSTALL=/usr/libexec/pennmush - # Where to install with 'make debianinstall' - DEB_INSTALL=$(DESTDIR)/usr/lib/pennmush/game - DEB_BIN=$(DESTDIR)/usr/games - all: config.h options.h autogen game/mush.cnf @echo "Making all in src." (cd src; make all "CC=$(CC)" "CCFLAGS=$(CCFLAGS)" \ --- 66,71 ---- *************** *** 297,317 **** @echo "** Files installed in $(GLOBAL_INSTALL). Feel free to move them." @echo "** You can run $(GLOBAL_INSTALL)/ln-dir.sh to create a user directory," @echo "** or symlink that to somewhere easier to run. You may wish to strip them." - - debianinstall: install - (cd game/txt; make clean compose.sh) - $(INSTALLDIR) $(DEB_INSTALL) - $(INSTALLDIR) $(DEB_BIN) - $(CP) -R game/* $(DEB_INSTALL) - -rm -f $(DEB_INSTALL)/netmush $(DEB_INSTALL)/info_slave - $(INSTALL) config.sh $(DEB_INSTALL)/config.sh - $(INSTALL) src/netmud $(DEB_INSTALL)/netmush - $(INSTALL) src/info_slave $(DEB_INSTALL)/info_slave - $(INSTALL) utils/penn-install $(DEB_BIN)/penn-install - $(CHMOD) a+rX -R $(DEB_INSTALL) - $(CHMOD) a+rX $(DEB_BIN)/penn-install - @echo "** Files installed in $(DEB_INSTALL)." - @echo "** You can run penn-install to create a user directory." !NO!SUBS! chmod 644 Makefile --- 293,298 ---- *** 1_7_7.1228/MANIFEST Thu, 02 Sep 2004 11:04:56 -0500 dunemush (pennmush/d/34_MANIFEST 1.21.1.2.1.7.1.3.1.29 600) --- 1_7_7.1248(w)/MANIFEST Wed, 03 Nov 2004 14:13:26 -0600 dunemush (pennmush/d/34_MANIFEST 1.21.1.2.1.7.1.3.1.30 600) *************** *** 163,169 **** utils/make_access_cnf.sh utils/mkcmds.sh.SH utils/mkvershlp.pl - utils/penn-install utils/update-cnf.pl utils/update.pl game/README --- 163,168 ---- *** 1_7_7.1228/win32/funs.h Sat, 17 Jul 2004 20:44:39 -0500 dunemush (pennmush/f/12_funs.h 1.11.1.9.2.8.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.3.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.10.1.1.1.4.1.5.1.1 640) --- 1_7_7.1248(w)/win32/funs.h Sun, 05 Dec 2004 22:50:58 -0600 dunemush (pennmush/f/12_funs.h 1.11.1.9.2.8.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.3.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.10.1.1.1.4.1.5.1.1.1.1 640) *************** *** 341,343 **** --- 341,344 ---- FUNCTION_PROTO(fun_zemit); FUNCTION_PROTO(fun_zfun); FUNCTION_PROTO(fun_zone); + FUNCTION_PROTO(fun_zwho); *** 1_7_7.1228/game/txt/hlp/pennvOLD.hlp Sun, 29 Aug 2004 15:58:41 -0500 dunemush (pennmush/g/30_pennvOLD.h 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.2.1.2 660) --- 1_7_7.1248(w)/game/txt/hlp/pennvOLD.hlp Sun, 05 Dec 2004 22:51:14 -0600 dunemush (pennmush/g/30_pennvOLD.h 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.2.1.3 660) *************** *** 4419,4425 **** 1.7.7: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, ! 36, 37, 38, 39 1.7.6: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 1.7.5: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 1.7.4: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, --- 4419,4425 ---- 1.7.7: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, ! 36, 37, 38, 39, 40 1.7.6: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 1.7.5: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 1.7.4: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, *** 1_7_7.1228/game/txt/hlp/pennv177.hlp Mon, 25 Oct 2004 17:41:11 -0500 dunemush (pennmush/g/34_pennv177.h 1.226.1.14.1.212 660) --- 1_7_7.1248(w)/game/txt/hlp/pennv177.hlp Sun, 05 Dec 2004 22:51:14 -0600 dunemush (pennmush/g/34_pennv177.h 1.226.1.14.1.224 660) *************** *** 1,4 **** ! & 1.7.7p39 & changes This is a list of changes in this patchlevel which are probably of interest to players. More information about new commands and functions --- 1,4 ---- ! & 1.7.7p40 & changes This is a list of changes in this patchlevel which are probably of interest to players. More information about new commands and functions *************** *** 11,16 **** --- 11,45 ---- A list of the patchlevels associated with each release can be read in 'help patchlevels'. + Version 1.7.7 patchlevel 40 December 1, 2004 + + Major Changes: + * Another pass at the chunk allocator! Simpler but effective. + Folks should, however, greatly reduce their chunk_migrate + value in mush.cnf -- we suggest '50'. [TAP] + Commands: + * @command/alias can alias commands. Patch by Walker@M*U*S*H. + Functions: + * zwho() and zmwho(). [EEH] + Minor Changes: + * utils/penn-install is no longer part of the PennMUSH distribution + (it's part of the Debian maintainer's stuff). [EEH] + * Inheritable @locks are inherited off of ancestor objects. + Suggested by Zith@Lovarii. [SW] + Fixes: + * Infinite recursion in @lock/examine fixed. Report by + Amy Kou'ai (Amy@ShoujoAi) and Sparta Kerleon (Sparta@ShoujoAi). [TAP] + * @flag/letter now allows setting a flag's letter to one used by + another flag that works on different object types. Report by Lenon. + * Translation corrections by Cheetah@M*U*S*H and [EEH]. + * Added __USE_POSIX to the cflags for linux, and removed + checking for -lbind, to help SuSE 9. Report by Ambrosia@M*U*S*H. + * Fixes to panic db loading logic. [SW] + * escape() shouldn't double-escape the first character of the + string when it's a special character. Report by Walker@M*U*S*H. + + + & 1.7.7p39 Version 1.7.7 patchlevel 39 October 25, 2004 Major Changes: *** 1_7_7.1228/src/chunk.c Tue, 08 Jun 2004 12:37:13 -0500 dunemush (pennmush/g/38_chunk.c 1.40 660) --- 1_7_7.1248(w)/src/chunk.c Sun, 05 Dec 2004 22:51:13 -0600 dunemush (pennmush/g/38_chunk.c 1.44 660) *************** *** 14,27 **** * * *

Basic operation:

! * The managed memory pool is divided into approximately 64K regions. ! * Each of these regions contain variable-size chunks representing ! * allocated and available (free) memory. No individual allocation ! * may be larger than will fit in a single region, and no allocation ! * may be smaller than two bytes. Each chunk has between two and four ! * bytes of overhead (indicating the used/free status, the size of the ! * chunk, and the number of dereferences for the chunk), and each ! * region has additional overhead of about 32 bytes. * * Allocations are made with the chunk_create() call, which is given * the size of the data, the data value to be stored, and an initial --- 14,27 ---- * * *

Basic operation:

! * The managed memory pool is divided into regions of approximately 64KB. ! * These regions contain variable-size chunks representing allocated and ! * available (free) memory. No individual allocation may be larger than ! * will fit in a single region, and no allocation may be smaller than one ! * byte. Each chunk has between two and four bytes of overhead (indicating ! * the used/free status, the size of the chunk, and the number of ! * dereferences for the chunk), and each region has additional overhead ! * of about 42 bytes. * * Allocations are made with the chunk_create() call, which is given * the size of the data, the data value to be stored, and an initial *************** *** 76,99 **** * the happiness of individual chunks will improve the happiness of the * whole. * ! * There are several things that factor into a chunk's happiness. * The things that make a chunk unhappy are: *
    - *
  • Having free space both before and after the chunk in its region. - *
  • Having only one allocated neighbor (or worse, none). The edges - * of a region count as allocated neighbors. *
  • Having a dereference count different from the region average. * The greater the difference, the more unhappy the chunk is. ! *
  • Being in a sparsely populated region. The more free space in a * region, the more unhappy the chunks in it. - *
  • Being away from the other chunks migrated at the same time. - * If some of the other chunks allowed to migrate are in the same - * region as a chunk, then it is happier. (This is specifically - * to improve locality during dumps.) *
! * None of these factors are absolute; all of them have different weights ! * that add into a general unhappiness for the chunk. The lower the ! * unhappiness, the better. * * Over time and usage, the dereference counts for chunks will increase * and eventually reach a maximum value of 255. (The count is limited --- 76,92 ---- * the happiness of individual chunks will improve the happiness of the * whole. * ! * There are two things that factor into a chunk's happiness. * The things that make a chunk unhappy are: *
    *
  • Having a dereference count different from the region average. * The greater the difference, the more unhappy the chunk is. ! *
  • Being in a sparsely populated region. The fewer chunks in a * region, the more unhappy the chunks in it. *
! * Neither of these factors are absolute; both of them have different ! * weights that add into a general unhappiness for the chunk. The lower ! * the unhappiness, the better. * * Over time and usage, the dereference counts for chunks will increase * and eventually reach a maximum value of 255. (The count is limited *************** *** 104,114 **** * number of regions exceeds 128, the 'migration period' is incremented * and all chunk dereference counts are halved. The critical number of * regions is determined based on the cache size and the total number of ! * regions. In general, period change should be controlled primarily by ! * the frequency of database dumps (which end up incrementing the dereference ! * count on all chunks, and thus all regions). Given a dump frequency ! * of once per hour (the default), there should be a period change about ! * every 2.6 days. * * *

Statistics:

--- 97,107 ---- * number of regions exceeds 128, the 'migration period' is incremented * and all chunk dereference counts are halved. The critical number of * regions is determined based on the cache size and the total number of ! * regions. If you're not using forking dumps, then period change should ! * be controlled primarily by the frequency of database dumps (which end ! * up incrementing the dereference count on all chunks, and thus all ! * regions). Given a dump frequency of once per hour (the default), there ! * should be a period change about every 2.6 days. * * *

Statistics:

*************** *** 218,224 **** --- 211,219 ---- #include "config.h" #include "conf.h" + #include #include + #include #include #include *************** *** 252,261 **** #undef CHUNK_PARANOID /** Log all moves and slides during migration. */ #undef DEBUG_CHUNK_MIGRATE - /** Log start, phases, and finish of migration. */ - #undef DEBUG_CHUNK_MIGRATE_LOUD - /** Log info about trying to fill holes. */ - #undef DEBUG_CHUNK_MIGRATE_FILL /** Log creation of regions. */ #undef DEBUG_CHUNK_REGION_CREATE /** Log paging of regions. */ --- 247,252 ---- *************** *** 263,268 **** --- 254,265 ---- /** Log all mallocs. */ #undef DEBUG_CHUNK_MALLOC + /** For debugging, we keep a rolling log of debug messages. + * These get dumped to disk if we're about to panic. + */ + #define ROLLING_LOG_SIZE 200 + #define ROLLING_LOG_ENTRY_LEN 1024 + /* debug... */ #ifdef CHUNK_DEBUG #define ASSERT(x) assert(x) *************** *** 290,295 **** --- 287,308 ---- */ #define MAX_CHUNK_LEN (16384-1) + /** Number of oddballs tracked in regions. + * This is used to figure out when we should pull regions in because + * we have an opportunity to migrate chunks that don't match. + * Relatively arbitrary; too low means you don't move things out + * enough, but boosting it too high wastes memory. + */ + #define NUM_ODDBALLS 10 + + /** Minimum disagreement to be an oddball. + * This is used to figure out when we should pull regions in because + * we have an opportunity to migrate chunks that don't match. + * Relatively arbitrary; too low means you don't move things out + * enough, but boosting it too high wastes migration time. + */ + #define ODDBALL_THRESHOLD 8 + /* * FIXME: pulling config variables out of my left ear. Fix later. */ *************** *** 298,319 **** /** How much does the region array grow by each time it has to grow? */ #define FIXME_REGION_ARRAY_INCREMENT 10 ! /* ! * FIXME: unhappiness constants pulled out of right ear. ! * Needs testing and tweaking. */ ! /** Constants affecting the way things are migrated around. ! * Details in the migration discussion. */ ! #define UNHAPPINESS_HARD 0 ! #define UNHAPPINESS_SOFT 4 ! #define UNHAPPINESS_MAYBE 32 ! #define UNHAPPINESS_TWO 0 ! #define UNHAPPINESS_ONE 2 ! #define UNHAPPINESS_ZERO 24 ! #define UNHAPPINESS_SPACE_DIVISOR 1024 ! #define UNHAPPINESS_DEREFS_DIVISOR 1 ! #define UNHAPPINESS_NEVER 0xffff ! #define UNHAPPINESS_AWAY_THRESHOLD 96 /* * Structures and Accessor Macros --- 311,340 ---- /** How much does the region array grow by each time it has to grow? */ #define FIXME_REGION_ARRAY_INCREMENT 10 ! /** Limit for when being a nearly-empty region counts against being ! * a good region. This is exponential: an empty region gets a penalty ! * of 1 << LONLINESS_LIMIT. A near-empty region gets a penalty of ! * 1 << (LONLINESS_LIMIT - used_count). ! * ! * Rationale: we don't want to reuse empty regions (or make new regions) ! * for trivialities. ! */ ! #define LONLINESS_LIMIT 5 ! ! /** Free space limit for when we consider making new regions. ! * The total free space must be less than this percent of capacity. ! * ! * Rationale: we don't want to waste memory with lots of extra regions. ! */ ! #define FREE_PERCENT_LIMIT 2 ! ! /** Bias for allocating chunks in a region that's already in memory. ! * Actually, this is a bias against allocating in swapped-out regions, ! * but that's a nit... ! * ! * Rationale: reduce the amount of paging during migration. */ ! #define IN_MEMORY_BIAS 4 /* * Structures and Accessor Macros *************** *** 445,451 **** #define ChunkReferenceToPointer(ref) \ ChunkPointer(ChunkReferenceToRegion((ref)), ChunkReferenceToOffset((ref))) - /* * Macros for probing and manipulating chunk headers */ --- 466,471 ---- *************** *** 500,505 **** --- 520,529 ---- #define ChunkNextFree(region, offset) \ ((ChunkDataPtr(region, offset)[0] << 8) + ChunkDerefs(region, offset)) + /* 0 for no, 1 for yes with room, 2 for exact */ + #define FitsInSpace(size, capacity) \ + (((size) == (capacity)) ? 2 : ((size) <= (capacity) - MIN_REMNANT_LEN)) + /** Region info that gets paged out with its region. * This is at the start of the region; * the rest of the 64K bytes of the region contain chunks. *************** *** 524,529 **** --- 548,554 ---- we don't page in regions to update counts on period change! */ RegionHeader *in_memory; /**< cache entry; NULL if paged out */ + u_int_16 oddballs[NUM_ODDBALLS]; /**< chunk offsets with odd derefs */ } Region; #define RegionDerefs(region) \ *************** *** 532,537 **** --- 557,566 ---- (curr_period - regions[(region)].period_last_touched)) / \ regions[(region)].used_count \ : 0) + #define RegionDerefsWithChunk(region, derefs) \ + (((regions[(region)].total_derefs >> \ + (curr_period - regions[(region)].period_last_touched)) + derefs) / \ + (regions[(region)].used_count + 1)) /* * Globals *************** *** 584,592 **** static int stat_page_out; /**< Number of page-outs */ static int stat_page_in; /**< Number of page-ins */ static int stat_migrate_slide; /**< Number of slide migrations */ ! static int stat_migrate_fill_exact; /**< Number of exact hole fillings */ ! static int stat_migrate_fill_inexact; /**< Number of inexact hole fillings */ ! static int stat_migrate_away; /**< Number of chunk expulsions */ static int stat_create; /**< Number of chunk creations */ static int stat_delete; /**< Number of chunk deletions */ --- 613,620 ---- static int stat_page_out; /**< Number of page-outs */ static int stat_page_in; /**< Number of page-ins */ static int stat_migrate_slide; /**< Number of slide migrations */ ! static int stat_migrate_move; /**< Number of move migrations */ ! static int stat_migrate_away; /**< Number of chunk evictions */ static int stat_create; /**< Number of chunk creations */ static int stat_delete; /**< Number of chunk deletions */ *************** *** 595,613 **** /* * migration globals that are used for holding relevant data... */ - static int m_alloc; /**< The allocated length for the arrays. */ static int m_count; /**< The used length for the arrays. */ static chunk_reference_t **m_references; /**< The passed-in references array. */ - static u_int_16 *m_length; /**< The lengths of the chunks to be moved. */ - static u_int_16 *m_unhappiness; /**< The unhappiness of the chunks. */ - static unsigned char *m_derefs; /**< The dereferences for the chunks. */ - static int m_limit; /**< The number of migrates allowed. */ /* * Debug routines */ #ifdef CHUNK_PARANOID /** Give a detailed map of a region. * Lists pertinent region information, and all the chunks in the region. * Does not print the contents of the chunks (which would probably be --- 623,702 ---- /* * migration globals that are used for holding relevant data... */ static int m_count; /**< The used length for the arrays. */ static chunk_reference_t **m_references; /**< The passed-in references array. */ + #ifdef CHUNK_PARANOID + /** Log of recent actions for debug purposes */ + static char rolling_log[ROLLING_LOG_SIZE][ROLLING_LOG_ENTRY_LEN]; + static int rolling_pos; + static int noisy_log = 0; + #endif + + /* + * Forward decls + */ + static void find_oddballs(u_int_16 region); /* * Debug routines */ + /** Add a message to the rolling log. */ + static void + debug_log(char const *format, ...) + { + #ifdef CHUNK_PARANOID + va_list args; + + va_start(args, format); + vsprintf(rolling_log[rolling_pos], format, args); + va_end(args); + + rolling_log[rolling_pos][ROLLING_LOG_ENTRY_LEN - 1] = '\0'; + if (noisy_log) { + fprintf(tracelog_fp, "%s\n", rolling_log[rolling_pos]); + fflush(tracelog_fp); + } + rolling_pos = (rolling_pos + 1) % ROLLING_LOG_SIZE; + #else + if (format) + return; /* shut up the compiler warning */ + #endif + } + #ifdef CHUNK_PARANOID + /** Dump the rolling log. */ + static void + dump_debug_log(FILE * fp) + { + int j; + fputs("Recent chunk activity:\n", fp); + j = rolling_pos; + do { + if (rolling_log[j][0]) { + fputs(rolling_log[j], fp); + fputc('\n', fp); + rolling_log[j][0] = '\0'; + } + j = (j + 1) % ROLLING_LOG_SIZE; + } while (j != rolling_pos); + fputs("End of recent chunk activity.\n", fp); + fflush(fp); + } + + /** Test if a chunk is migratable. */ + static int + migratable(u_int_16 region, u_int_16 offset) + { + chunk_reference_t ref = ChunkReference(region, offset); + int j; + + for (j = 0; j < m_count; j++) + if (m_references[j][0] == ref) + return 1; + return 0; + } + /** Give a detailed map of a region. * Lists pertinent region information, and all the chunks in the region. * Does not print the contents of the chunks (which would probably be *************** *** 640,646 **** if (rhp) { for (offset = FIRST_CHUNK_OFFSET_IN_REGION; offset < REGION_SIZE; offset += ChunkFullLen(region, offset)) { ! fprintf(fp, "chunk: %4s %-6s off:%04x full:%04x ", ChunkIsFree(region, offset) ? "FREE" : "", ChunkIsShort(region, offset) ? "SHORT" : (ChunkIsMedium(region, offset) ? "MEDIUM" : "LONG"), --- 729,736 ---- if (rhp) { for (offset = FIRST_CHUNK_OFFSET_IN_REGION; offset < REGION_SIZE; offset += ChunkFullLen(region, offset)) { ! fprintf(fp, "chunk:%c%4s %-6s off:%04x full:%04x ", ! migratable(region, offset) ? '*' : ' ', ChunkIsFree(region, offset) ? "FREE" : "", ChunkIsShort(region, offset) ? "SHORT" : (ChunkIsMedium(region, offset) ? "MEDIUM" : "LONG"), *************** *** 677,687 **** for (pos = FIRST_CHUNK_OFFSET_IN_REGION; pos < REGION_SIZE; pos += ChunkFullLen(region, pos)) { if (pos == offset) { ! if ((ChunkPointer(region, pos)[0] & CHUNK_FREE_MASK) == CHUNK_FREE) ! mush_panic("Invalid reference to used chunk"); return; } } } /** Verify that a region is sane. --- 767,778 ---- for (pos = FIRST_CHUNK_OFFSET_IN_REGION; pos < REGION_SIZE; pos += ChunkFullLen(region, pos)) { if (pos == offset) { ! if (ChunkIsFree(region, pos)) ! mush_panic("Invalid reference to free chunk as used"); return; } } + mush_panic("Invalid reference to non-chunk as used"); } /** Verify that a region is sane. *************** *** 1054,1063 **** * \param region the region to allocate in. * \param offset the offset of the hole to use. * \param full_len the length (including headers) of the space to allocate. * \return the offset of the allocated space. */ static u_int_16 ! split_hole(u_int_16 region, u_int_16 offset, u_int_16 full_len) { Region *rp = regions + region; u_int_16 hole_len = ChunkFullLen(region, offset); --- 1145,1155 ---- * \param region the region to allocate in. * \param offset the offset of the hole to use. * \param full_len the length (including headers) of the space to allocate. + * \param align the alignment to use: 0 = easiest, 1 = left, 2 = right. * \return the offset of the allocated space. */ static u_int_16 ! split_hole(u_int_16 region, u_int_16 offset, u_int_16 full_len, int align) { Region *rp = regions + region; u_int_16 hole_len = ChunkFullLen(region, offset); *************** *** 1097,1107 **** } ASSERT(hole_len >= full_len + MIN_REMNANT_LEN); ! if (rp->in_memory->first_free == offset) { rp->free_bytes -= full_len; - rp->in_memory->first_free += full_len; write_free_chunk(region, offset + full_len, hole_len - full_len, ChunkNextFree(region, offset)); if (rp->largest_free_chunk == hole_len) rp->largest_free_chunk = largest_hole(region); return offset; --- 1189,1215 ---- } ASSERT(hole_len >= full_len + MIN_REMNANT_LEN); ! if (!align) { ! if (rp->in_memory->first_free == offset) ! align = 1; ! else ! align = 2; ! } ! if (align == 1) { rp->free_bytes -= full_len; write_free_chunk(region, offset + full_len, hole_len - full_len, ChunkNextFree(region, offset)); + if (rp->in_memory->first_free == offset) + rp->in_memory->first_free += full_len; + else { + u_int_16 hole; + for (hole = rp->in_memory->first_free; + hole; hole = ChunkNextFree(region, hole)) + if (ChunkNextFree(region, hole) == offset) + break; + ASSERT(hole); + write_next_free(region, hole, offset + full_len); + } if (rp->largest_free_chunk == hole_len) rp->largest_free_chunk = largest_hole(region); return offset; *************** *** 1133,1138 **** --- 1241,1248 ---- size_t remaining; int done; + debug_log("read_cache_region %04x", region); + /* Try to seek up to 3 times... */ for (j = 0; j < 3; j++) #ifdef WIN32 *************** *** 1180,1186 **** #endif } ! /** Write a region from a file. * \param fd file to write to * \param rhp region buffer to use * \param region region to write --- 1290,1296 ---- #endif } ! /** Write a region to a file. * \param fd file to write to * \param rhp region buffer to use * \param region region to write *************** *** 1194,1199 **** --- 1304,1311 ---- size_t remaining; int done; + debug_log("write_cache_region %04x", region); + /* Try to seek up to 3 times... */ for (j = 0; j < 3; j++) #ifdef WIN32 *************** *** 1243,1248 **** --- 1355,1386 ---- #endif } + /** Update cache position to stave off recycling. + * \param rhp the cached region to keep around. + */ + static void + touch_cache_region(RegionHeader * rhp) + { + debug_log("touch_cache_region %04x", rhp->region_id); + + if (cache_head == rhp) + return; + if (cache_tail == rhp) + cache_tail = rhp->prev; + if (rhp->prev) + rhp->prev->next = rhp->next; + if (rhp->next) + rhp->next->prev = rhp->prev; + + if (cache_head) + cache_head->prev = rhp; + rhp->next = cache_head; + rhp->prev = NULL; + cache_head = rhp; + if (!cache_tail) + cache_tail = rhp; + } + /** Find space in the cache. * This is likely to require paging out something. * \return a pointer to an available cache region. *************** *** 1252,1257 **** --- 1390,1397 ---- { RegionHeader *rhp; + debug_log("find_available_cache_region"); + if (!cache_tail || cached_region_count * REGION_SIZE < (unsigned) CHUNK_CACHE_MEMORY) { /* first use ... normal case if empty ... so allocate space */ *************** *** 1271,1316 **** if (cache_tail->region_id == INVALID_REGION_ID) return cache_tail; /* page the current occupant out */ #ifdef DEBUG_CHUNK_PAGING do_rawlog(LT_TRACE, "CHUNK: Paging out region %04x (offset %08x)", ! cache_tail->region_id, (unsigned) file_offset); #endif ! write_cache_region(swap_fd, cache_tail, cache_tail->region_id); /* keep statistics */ ! stat_paging_histogram[RegionDerefs(cache_tail->region_id)]++; stat_page_out++; /* mark the paged out region as not in memory */ ! regions[cache_tail->region_id].in_memory = NULL; /* mark it not in use for sanity check reasons */ ! cache_tail->region_id = INVALID_REGION_ID; ! ! return cache_tail; ! } ! ! /** Update cache position to stave off recycling. ! * \param rhp the cached region to keep around. ! */ ! static void ! touch_cache_region(RegionHeader * rhp) ! { ! if (cache_head == rhp) ! return; ! if (cache_tail == rhp) ! cache_tail = rhp->prev; ! if (rhp->prev) ! rhp->prev->next = rhp->next; ! if (rhp->next) ! rhp->next->prev = rhp->prev; ! if (cache_head) ! cache_head->prev = rhp; ! rhp->next = cache_head; ! rhp->prev = NULL; ! cache_head = rhp; ! if (!cache_tail) ! cache_tail = rhp; } /** Bring a paged out region back into memory. --- 1411,1434 ---- if (cache_tail->region_id == INVALID_REGION_ID) return cache_tail; + rhp = cache_tail; /* page the current occupant out */ + find_oddballs(rhp->region_id); #ifdef DEBUG_CHUNK_PAGING do_rawlog(LT_TRACE, "CHUNK: Paging out region %04x (offset %08x)", ! rhp->region_id, (unsigned) file_offset); #endif ! write_cache_region(swap_fd, rhp, rhp->region_id); /* keep statistics */ ! stat_paging_histogram[RegionDerefs(rhp->region_id)]++; stat_page_out++; /* mark the paged out region as not in memory */ ! regions[rhp->region_id].in_memory = NULL; /* mark it not in use for sanity check reasons */ ! rhp->region_id = INVALID_REGION_ID; ! return rhp; } /** Bring a paged out region back into memory. *************** *** 1325,1330 **** --- 1443,1451 ---- u_int_32 offset; unsigned int shift; + debug_log("bring_in_region %04x", region); + + ASSERT(region < region_count); if (rp->in_memory) return; rhp = find_available_cache_region(); *************** *** 1423,1535 **** return region; } ! /** Compute the expected unhappiness for a chunk in a region. ! * This is used to figure out which region is the best region to hold ! * a chunk, either when it is new, or when it's really unhappy where it is. ! * If the region is in memory, then the free list is searched for a good ! * place to put the chunk; otherwise an educated guess is made based on ! * the free count, free bytes, and largest free chunk. ! * \param region the prospective region to hold the chunk. ! * \param full_len the length (including headers) of the chunk. ! * \param derefs the number of dereferences on the chunk. ! * \return the unhappiness rating if the chunk was placed here. */ ! static u_int_16 ! predict_score(u_int_16 region, u_int_16 full_len, unsigned char derefs) { Region *rp = regions + region; ! u_int_16 score; ! if (rp->free_bytes < full_len) ! return UNHAPPINESS_NEVER; ! score = 0; ! if (rp->in_memory) { ! u_int_16 offset, best_offset; ! score = UNHAPPINESS_NEVER; ! best_offset = 0; ! for (offset = rp->in_memory->first_free; ! offset; offset = ChunkNextFree(region, offset)) { ! if (ChunkFullLen(region, offset) == full_len) { ! score = UNHAPPINESS_TWO; ! best_offset = offset; ! if (best_offset == rp->in_memory->first_free) ! break; ! } else if (ChunkFullLen(region, offset) >= full_len + MIN_REMNANT_LEN && ! score >= UNHAPPINESS_ONE) { ! if (score == UNHAPPINESS_ONE && ! best_offset == rp->in_memory->first_free) ! continue; ! score = UNHAPPINESS_ONE; ! best_offset = offset; ! } ! } ! if (score == UNHAPPINESS_NEVER) ! return UNHAPPINESS_NEVER; ! if (rp->in_memory->first_free == best_offset || ! !ChunkNextFree(region, best_offset)) ! score += UNHAPPINESS_HARD; ! else ! score += UNHAPPINESS_SOFT; ! } else { ! switch (rp->free_count) { ! case 1: ! if (rp->free_bytes == full_len) { ! score += UNHAPPINESS_HARD + UNHAPPINESS_TWO; ! } else { ! if (rp->free_bytes < full_len + MIN_REMNANT_LEN) ! return UNHAPPINESS_NEVER; ! score += UNHAPPINESS_HARD + UNHAPPINESS_ONE; ! } ! break; ! case 2: ! if (rp->largest_free_chunk == full_len || ! rp->free_bytes - rp->largest_free_chunk == full_len) { ! score += UNHAPPINESS_HARD + UNHAPPINESS_TWO; ! } else { ! if (rp->largest_free_chunk < full_len + MIN_REMNANT_LEN) ! return UNHAPPINESS_NEVER; ! score += UNHAPPINESS_HARD + UNHAPPINESS_ONE; ! } ! break; ! default: ! if (rp->largest_free_chunk == full_len) { ! score += UNHAPPINESS_SOFT + UNHAPPINESS_TWO; ! } else { ! if (rp->largest_free_chunk < full_len) ! return UNHAPPINESS_NEVER; ! if (rp->largest_free_chunk < full_len + MIN_REMNANT_LEN) { ! if (rp->free_bytes - rp->largest_free_chunk < ! full_len + (MIN_REMNANT_LEN * (rp->free_count - 2))) ! return UNHAPPINESS_NEVER; ! score += UNHAPPINESS_MAYBE + UNHAPPINESS_ONE; ! } else ! score += UNHAPPINESS_SOFT + UNHAPPINESS_ONE; ! } ! break; ! } ! } ! score += (rp->free_bytes - full_len) / UNHAPPINESS_SPACE_DIVISOR; ! score += abs((rp->total_derefs + derefs) / (rp->used_count + 1) - derefs) / ! UNHAPPINESS_DEREFS_DIVISOR; ! ! if (m_references) { ! int lonliness, j; ! lonliness = 8; ! for (j = 0; j < m_count; j++) ! if (region == ChunkReferenceToRegion(m_references[j][0])) ! break; ! for (; j < m_count; j++) { ! if (region != ChunkReferenceToRegion(m_references[j][0])) ! break; ! lonliness--; ! if (!lonliness) break; } ! score += lonliness; } - - return score; } /** Find the best region to hold a chunk. --- 1544,1588 ---- return region; } ! /** Find the oddball chunks in a region. ! * \param region the region to search in. */ ! static void ! find_oddballs(u_int_16 region) { Region *rp = regions + region; ! int j, d1, d2; ! u_int_16 offset, len; ! int mean; ! for (j = 0; j < NUM_ODDBALLS; j++) ! rp->oddballs[j] = 0; ! mean = RegionDerefs(region); ! ! for (offset = FIRST_CHUNK_OFFSET_IN_REGION; ! offset < REGION_SIZE; offset += len) { ! len = ChunkFullLen(region, offset); ! if (ChunkIsFree(region, offset)) ! continue; ! d1 = abs(mean - ChunkDerefs(region, offset)); ! if (d1 < ODDBALL_THRESHOLD) ! continue; ! j = NUM_ODDBALLS; ! while (j--) { ! if (!rp->oddballs[j]) ! continue; ! d2 = abs(mean - ChunkDerefs(region, rp->oddballs[j])); ! if (d1 < d2) break; + if (j < NUM_ODDBALLS - 1) + rp->oddballs[j + 1] = rp->oddballs[j]; } ! j++; ! if (j >= NUM_ODDBALLS) ! continue; ! rp->oddballs[j] = offset; } } /** Find the best region to hold a chunk. *************** *** 1540,2150 **** * sufficiently unhappy. * \param full_len the size of the chunk, including headers. * \param derefs the number of dereferences on the chunk. ! * \param limit the maximum allowed unhappiness + 1. * \return the region id for the least unhappy region. */ static u_int_16 ! find_best_region(u_int_16 full_len, unsigned char derefs, u_int_16 limit) { - u_int_16 best_score, score; u_int_16 best_region, region; ! best_region = region_count; ! best_score = limit; for (region = 0; region < region_count; region++) { ! score = predict_score(region, full_len, derefs); if (best_score > score) { best_score = score; best_region = region; } } return best_region; } /** Find the best offset in a region to hold a chunk. ! * The region must be in memory before calling. * \param region the region to allocate in. ! * \param full_len the length of the chunk, includig headers. */ static u_int_16 ! find_best_offset(u_int_16 region, u_int_16 full_len) { ! Region *rp = regions + region; ! u_int_16 score; ! u_int_16 offset, best_offset; ! if (rp->free_bytes < full_len) ! return 0; ! score = UNHAPPINESS_NEVER; ! best_offset = 0; ! for (offset = rp->in_memory->first_free; ! offset; offset = ChunkNextFree(region, offset)) { ! if (ChunkFullLen(region, offset) == full_len) { ! if (offset == rp->in_memory->first_free) ! return offset; ! score = UNHAPPINESS_TWO; ! best_offset = offset; ! } else if (ChunkFullLen(region, offset) >= full_len + MIN_REMNANT_LEN && ! score >= UNHAPPINESS_ONE) { ! if (score == UNHAPPINESS_ONE && best_offset == rp->in_memory->first_free) ! continue; ! score = UNHAPPINESS_ONE; ! best_offset = offset; } } ! return best_offset; } /* ! * Utility Routines - Migration */ ! /** Resort the migratable chunk info. ! * Keeping the migration array sorted makes a few operations easier. */ ! static void ! migrate_sort(void) { ! int j, k; ! chunk_reference_t *t_r; ! u_int_16 t_l, t_u; ! unsigned char t_d; ! for (j = 1; j < m_count; j++) { ! t_r = m_references[j]; ! t_l = m_length[j]; ! t_u = m_unhappiness[j]; ! t_d = m_derefs[j]; ! for (k = j; k--;) { ! if (m_references[k][0] < *t_r) ! break; ! m_references[k + 1] = m_references[k]; ! m_length[k + 1] = m_length[k]; ! m_unhappiness[k + 1] = m_unhappiness[k]; ! m_derefs[k + 1] = m_derefs[k]; ! } ! m_references[k + 1] = t_r; ! m_length[k + 1] = t_l; ! m_unhappiness[k + 1] = t_u; ! m_derefs[k + 1] = t_d; } } ! /** Get vital statistics for all migratable chunks in a region. ! * This includes the current unhappiness rating. ! * \param region the region to be scanned. */ ! static void ! migrate_scan_region(u_int_16 region) { ! Region *rp = regions + region; ! int j, k; ! u_int_16 offset, f_offset; ! u_int_16 lonliness; ! ! ASSERT(region < region_count); ! #ifdef CHUNK_PARANOID ! if (!region_is_valid(region)) ! mush_panic("Invalid region in migrate_scan_region!"); ! #endif ! ! if (!rp->in_memory) ! return; ! for (j = 0; j < m_count; j++) { ! if (region == ChunkReferenceToRegion(m_references[j][0])) ! break; ! } ! if (j >= m_count) ! return; ! ! if (!m_length[j]) { ! /* never seen this region before in this migration, so check to make ! * sure all the chunks are valid... */ ! u_int_16 pos; ! ! k = j; ! pos = FIRST_CHUNK_OFFSET_IN_REGION; ! while (k < m_count && region == ChunkReferenceToRegion(m_references[k][0])) { ! offset = ChunkReferenceToOffset(m_references[k][0]); ! while (pos < offset && pos < REGION_SIZE) ! pos += ChunkFullLen(region, pos); ! if (pos != offset || ! (ChunkPointer(region, pos)[0] & CHUNK_FREE_MASK) == CHUNK_FREE) ! mush_panic("Invalid reference to used chunk in migrate"); ! k++; ! } ! } ! ! /* count friends in region, up to 8 */ ! lonliness = 8; ! for (k = j + 1; k < m_count; k++) { ! if (region != ChunkReferenceToRegion(m_references[k][0])) ! break; ! lonliness--; ! if (!lonliness) ! break; ! } ! f_offset = rp->in_memory->first_free; ! while (j < m_count && region == ChunkReferenceToRegion(m_references[j][0])) { ! offset = ChunkReferenceToOffset(m_references[j][0]); ! m_length[j] = ChunkFullLen(region, offset); ! m_derefs[j] = ChunkDerefs(region, offset); ! if (!f_offset || f_offset > offset) { ! m_unhappiness[j] = 1; ! } else { ! m_unhappiness[j] = 1; ! do { ! if (f_offset + ChunkFullLen(region, f_offset) == offset) ! m_unhappiness[j] = 0; ! f_offset = ChunkNextFree(region, f_offset); ! } while (f_offset && f_offset < offset); ! } ! if (offset + m_length[j] != f_offset) { ! m_unhappiness[j]++; ! } ! switch (m_unhappiness[j]) { ! case 0: ! m_unhappiness[j] = UNHAPPINESS_ZERO; ! break; ! case 1: ! m_unhappiness[j] = UNHAPPINESS_ONE; ! break; ! case 2: ! m_unhappiness[j] = UNHAPPINESS_TWO; ! break; ! } ! if (!f_offset || offset < rp->in_memory->first_free) ! m_unhappiness[j] += UNHAPPINESS_HARD; ! else ! m_unhappiness[j] += UNHAPPINESS_SOFT; ! m_unhappiness[j] += rp->free_bytes / UNHAPPINESS_SPACE_DIVISOR; ! m_unhappiness[j] += abs(RegionDerefs(region) - m_derefs[j]) / ! UNHAPPINESS_DEREFS_DIVISOR; ! m_unhappiness[j] += lonliness; ! j++; } } ! /** Prep the temporary migration storage. ! * This includes allocating space for the arrays, zeroing them out, ! * and scanning all the regions in memory. ! * \param count the number of chunks in this migration pass. ! * \param references the array of chunk reference pointers. */ static void ! migrate_prep(int count, chunk_reference_t ** references) { ! int j; ! u_int_16 region; ! if (m_alloc < count) { ! if (m_alloc) { ! #ifdef DEBUG_CHUNK_MALLOC ! do_rawlog(LT_TRACE, "CHUNK: free()ing migration arrays"); ! #endif ! mush_free((Malloc_t) m_length, "migrate length"); ! mush_free((Malloc_t) m_unhappiness, "migrate unhappiness"); ! mush_free((Malloc_t) m_derefs, "migrate derefs"); ! } ! #ifdef DEBUG_CHUNK_MALLOC ! do_rawlog(LT_TRACE, "CHUNK: malloc()ing migration arrays"); ! #endif ! m_length = mush_malloc(count * sizeof(*m_length), "migrate length"); ! m_unhappiness = ! mush_malloc(count * sizeof(*m_unhappiness), "migrate unhappiness"); ! m_derefs = mush_malloc(count * sizeof(*m_derefs), "migrate derefs"); ! if (!m_length || !m_unhappiness || !m_derefs) ! mush_panic("Cannot allocate working space for migration"); ! m_alloc = count; ! } ! m_count = count; ! m_references = references; ! for (j = 0; j < count; j++) { ! m_length[j] = 0; ! m_unhappiness[j] = UNHAPPINESS_NEVER; ! m_derefs[j] = 0; } ! ! migrate_sort(); ! ! region = INVALID_REGION_ID; ! for (j = 0; j < count; j++) { ! if (region == ChunkReferenceToRegion(m_references[j][0])) ! continue; ! region = ChunkReferenceToRegion(m_references[j][0]); ! migrate_scan_region(region); ! } ! } ! ! /** Slide an allocated chunk over into a neighboring free space. ! * \param region the region of the free space. ! * \param offset the offset of the free space. ! * \param which the index (in the migration arrays) of the chunk to move. ! */ ! static void ! migrate_slide(u_int_16 region, u_int_16 offset, int which) ! { ! Region *rp = regions + region; ! u_int_16 len, next, other, prev; ! ! len = ChunkFullLen(region, offset); ! next = ChunkNextFree(region, offset); ! other = ChunkReferenceToOffset(m_references[which][0]); ! if (other > offset) { ! memmove(ChunkPointer(region, offset), ChunkPointer(region, other), ! m_length[which]); ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: Sliding chunk %08x to %04x%04x", ! m_references[which][0], region, offset); ! #endif ! m_references[which][0] = ChunkReference(region, offset); ! other = offset + m_length[which]; ! } else { ! prev = offset + len - m_length[which]; ! memmove(ChunkPointer(region, prev), ChunkPointer(region, other), ! m_length[which]); ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: Sliding chunk %08x to %04x%04x", ! m_references[which][0], region, prev); ! #endif ! m_references[which][0] = ChunkReference(region, prev); ! } ! write_free_chunk(region, other, len, next); ! coalesce_frees(region, other); ! if (rp->in_memory->first_free == offset) { ! rp->in_memory->first_free = other; ! } else { ! prev = rp->in_memory->first_free; ! while (prev && ChunkNextFree(region, prev) != offset) ! prev = ChunkNextFree(region, prev); ! write_next_free(region, prev, other); ! coalesce_frees(region, prev); ! } ! ! stat_migrate_slide++; ! } ! ! /** Move an allocated chunk into a free hole. ! * \param region the region of the free space. ! * \param offset the offset of the free space. ! * \param which the index (in the migration arrays) of the chunk to move. ! */ ! static void ! migrate_move(u_int_16 region, u_int_16 offset, int which) ! { ! Region *rp = regions + region; ! u_int_16 s_reg, s_off; ! Region *srp; ! ! s_reg = ChunkReferenceToRegion(m_references[which][0]); ! s_off = ChunkReferenceToOffset(m_references[which][0]); ! srp = regions + s_reg; ! ! bring_in_region(region); ! if (!srp->in_memory) { ! touch_cache_region(rp->in_memory); ! bring_in_region(s_reg); ! touch_cache_region(rp->in_memory); ! } ! offset = split_hole(region, offset, m_length[which]); ! memcpy(ChunkPointer(region, offset), ChunkPointer(s_reg, s_off), ! m_length[which]); ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: moving chunk %08x to %04x%04x", ! m_references[which][0], region, offset); ! #endif ! m_references[which][0] = ChunkReference(region, offset); ! rp->total_derefs += m_derefs[which]; ! free_chunk(s_reg, s_off); ! } ! ! /** Fill a hole. ! * Calling this only suggests that the hole might need filling; it does ! * not guarantee that the hole gets filled. It'll only get filled if ! * there's a chunk that would be happier in the hole than in its current ! * location. ! * \param region the region of the hole. ! * \param offset the offset of the hole. ! * \return true if the hole got something put into it. ! */ ! static int ! migrate_fill(u_int_16 region, u_int_16 offset) ! { ! Region *rp = regions + region; ! u_int_16 len; ! int j, best = 0; ! u_int_16 base, score, b_un = 0; ! u_int_16 lonliness; ! ! ASSERT(region < region_count); ! ASSERT(rp->in_memory); ! len = ChunkFullLen(region, offset); ! ! if (offset == rp->in_memory->first_free || !ChunkNextFree(region, offset)) ! base = UNHAPPINESS_HARD; ! else ! base = UNHAPPINESS_SOFT; ! ! #ifdef DEBUG_CHUNK_MIGRATE_FILL ! if (len < rp->largest_free_chunk) ! do_rawlog(LT_TRACE, "CHUNK: migrate_fill working on %4s %04x%04x, len %04x", ! (base == UNHAPPINESS_HARD) ? "HARD" : "SOFT", region, offset, ! len); ! #endif ! ! /* count friends in region, up to 8 */ ! lonliness = 8; ! for (j = 0; j < m_count; j++) ! if (region == ChunkReferenceToRegion(m_references[j][0])) ! break; ! for (; j < m_count; j++) { ! if (region != ChunkReferenceToRegion(m_references[j][0])) ! break; ! lonliness--; ! if (!lonliness) ! break; ! } ! ! for (j = 0; j < m_count; j++) { ! /* Skip the chunk if we haven't scanned it, ! * or it's in the same region and before or immediately after the hole. ! * Same region and before is forbidden to prevent loops, and ! * immediately after is forbidden because it should be a slide, ! * not a move. */ ! if (!m_length[j] || ! (m_references[j][0] > ChunkReference(region, 0) && ! m_references[j][0] <= ChunkReference(region, offset + len))) ! continue; ! if (base == UNHAPPINESS_HARD && ! ChunkReferenceToRegion(m_references[j][0]) == region) { ! /* If we're contemplating moving something after the last hole ! * in the same region, then the move would cause it to be soft, ! * not hard... unless there's only one hole. */ ! if (offset == rp->in_memory->first_free) ! score = UNHAPPINESS_HARD; ! else ! score = UNHAPPINESS_SOFT; ! } else { ! score = base; ! } ! if (len == m_length[j]) ! score += UNHAPPINESS_TWO; ! else if (len >= m_length[j] + MIN_REMNANT_LEN) ! score += UNHAPPINESS_ONE; ! else ! continue; ! score += (rp->free_bytes - m_length[j]) / UNHAPPINESS_SPACE_DIVISOR; ! score += abs((rp->total_derefs + m_derefs[j]) / (rp->used_count + 1) - ! m_derefs[j]) / UNHAPPINESS_DEREFS_DIVISOR; ! score += lonliness; ! if (score < m_unhappiness[j]) { ! #ifdef DEBUG_CHUNK_MIGRATE_FILL ! if (len < rp->largest_free_chunk) ! do_rawlog(LT_TRACE, ! "CHUNK: migrate_fill looking at %08x, len %04x, unhappiness %04x, score %04x", ! m_references[j][0], m_length[j], m_unhappiness[j], score); ! #endif ! ! score = m_unhappiness[j] - score; ! if (b_un < score) { ! b_un = score; ! best = j; ! } ! } ! } ! ! if (!b_un) { ! /* Couldn't find anything worth moving. Maybe a slide? */ ! if (rp->free_bytes == rp->largest_free_chunk) { ! /* There's only one hole. Why bother moving it? */ ! return 0; ! } ! if (base == UNHAPPINESS_SOFT || offset == rp->in_memory->first_free) { ! /* Slide towards the front for anything except the last hole. */ ! chunk_reference_t target = ChunkReference(region, offset + len); ! for (j = 0; j < m_count; j++) ! if (m_references[j][0] == target) ! break; ! if (j >= m_count) ! return 0; ! } else { ! /* Slide towards the back. */ ! chunk_reference_t target = ChunkReference(region, offset); ! for (j = 0; j < m_count; j++) ! if (m_references[j][0] + m_length[j] == target) ! break; ! if (j >= m_count) ! return 0; ! } ! migrate_slide(region, offset, j); ! migrate_scan_region(region); ! return 1; ! } ! ! if (m_length[best] == len) ! stat_migrate_fill_exact++; ! else ! stat_migrate_fill_inexact++; ! ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: Moving: unhappiness was %04x, score %04x", ! m_unhappiness[best], b_un); ! #endif ! base = ChunkReferenceToRegion(m_references[best][0]); ! migrate_move(region, offset, best); ! migrate_sort(); ! migrate_scan_region(region); ! migrate_scan_region(base); ! return 1; ! } ! ! /** Migrate chunk away. ! * If the indicated chunk is sufficiently unwanted and unloved in its ! * current position, then it deserves a new home. Find it one, creating ! * a new region if * need be. ! * \param which the index (in the migration arrays) of the chunk to evict. ! * \return true if the chunk was moved. ! */ ! static int ! migrate_away(int which) ! { ! u_int_16 region, offset, s_reg; ! ! if (!m_length[which] || m_unhappiness[which] < UNHAPPINESS_AWAY_THRESHOLD) ! return 0; ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: Away: unhappiness was %04x", ! m_unhappiness[which]); ! #endif ! ! region = find_best_region(m_length[which], m_derefs[which], ! UNHAPPINESS_AWAY_THRESHOLD); ! s_reg = ChunkReferenceToRegion(m_references[which][0]); ! if (region >= region_count || region == s_reg) ! region = create_region(); ! else ! bring_in_region(region); ! offset = find_best_offset(region, m_length[which]); ! if (!offset) { ! region = create_region(); ! offset = FIRST_CHUNK_OFFSET_IN_REGION; ! } ! stat_migrate_away++; ! migrate_move(region, offset, which); ! migrate_sort(); ! migrate_scan_region(region); ! migrate_scan_region(s_reg); ! return 1; ! } ! ! /** Try to hardpack a region. ! * Fill chunks into the first and last free holes in a region. ! * \param region the region to fill. ! */ ! static int ! migrate_hardpack(u_int_16 region) ! { ! int worked = 0; ! Region *rp = regions + region; ! ! if (!m_limit) ! return 0; ! ! if (!rp->in_memory) { ! bring_in_region(region); ! migrate_scan_region(region); ! } ! ! while (m_limit && rp->free_bytes) ! if (migrate_fill(region, rp->in_memory->first_free)) { ! worked = 1; ! m_limit--; ! } else ! break; ! ! while (m_limit && rp->free_bytes) { ! u_int_16 offset = rp->in_memory->first_free; ! while (ChunkNextFree(region, offset)) ! offset = ChunkNextFree(region, offset); ! if (migrate_fill(region, offset)) { ! worked = 1; ! m_limit--; ! } else ! break; ! } ! ! return worked; ! } ! ! /** Try to softpack a region. ! * Fill chunks into any of the free holes in a region. ! * \param region the region to fill. ! */ ! static int ! migrate_softpack(u_int_16 region) ! { ! u_int_16 offset; ! int worked = 0; ! Region *rp = regions + region; ! ! if (!m_limit) ! return 0; ! ! if (!rp->in_memory) { ! bring_in_region(region); ! migrate_scan_region(region); ! } ! ! offset = rp->in_memory->first_free; ! while (m_limit && offset) ! if (migrate_fill(region, offset)) { ! worked = 1; ! m_limit--; ! offset = rp->in_memory->first_free; ! } else ! offset = ChunkNextFree(region, offset); ! ! return worked; ! } ! ! ! /* ! * Utility Routines - Statistics and debugging ! */ ! /** Display statistics to a player, or dump them to a log ! */ ! #define STAT_OUT(x) \ ! do { \ ! s = (x); \ ! if (GoodObject(player)) \ ! notify(player, s); \ ! else \ ! do_rawlog(LT_TRACE, "%s", s); \ ! } while (0) ! ! /** Display the stats summary page. ! * \param player the player to display it to, or NOTHING to log it. ! */ ! static void ! chunk_statistics(dbref player) ! { ! const char *s; ! int overhead; ! int free_count = 0; ! int free_bytes = 0; ! int free_large = 0; ! int used_count = 0; ! int used_bytes = 0; ! u_int_16 rid; ! ! for (rid = 0; rid < region_count; rid++) { ! free_count += regions[rid].free_count; ! free_bytes += regions[rid].free_bytes; ! free_large += regions[rid].largest_free_chunk; ! used_count += regions[rid].used_count; ! } ! used_bytes = (REGION_CAPACITY * region_count) - free_bytes; if (!GoodObject(player)) { do_rawlog(LT_TRACE, "---- Chunk statistics"); --- 1593,1755 ---- * sufficiently unhappy. * \param full_len the size of the chunk, including headers. * \param derefs the number of dereferences on the chunk. ! * \param old_region the region the chunk was in before (if any). * \return the region id for the least unhappy region. */ static u_int_16 ! find_best_region(u_int_16 full_len, int derefs, u_int_16 old_region) { u_int_16 best_region, region; ! int best_score, score; ! int free_bytes; ! Region *rp; ! ! best_region = INVALID_REGION_ID; ! best_score = INT_MAX; ! free_bytes = 0; for (region = 0; region < region_count; region++) { ! rp = regions + region; ! free_bytes += rp->free_bytes; ! if (!FitsInSpace(full_len, rp->largest_free_chunk) && ! !(rp->free_count == 2 && ! rp->free_bytes - rp->largest_free_chunk == full_len)) ! continue; ! ! if (region == old_region) ! score = derefs - RegionDerefs(region); ! else ! score = derefs - RegionDerefsWithChunk(region, derefs); ! if (score < 0) ! score = -score; ! if (!rp->in_memory) ! score += IN_MEMORY_BIAS; ! if (rp->used_count <= LONLINESS_LIMIT) ! score += 1 << (LONLINESS_LIMIT - rp->used_count); ! if (best_score > score) { best_score = score; best_region = region; } } + + if (best_region == INVALID_REGION_ID) { + #ifdef DEBUG_CHUNK_REGION_CREATE + do_rawlog(LT_TRACE, "find_best_region had to create region %04x", region); + #endif + best_region = create_region(); + } else if (best_score > (1 << LONLINESS_LIMIT) + IN_MEMORY_BIAS && + (free_bytes * 100 / (REGION_CAPACITY * region_count)) < + FREE_PERCENT_LIMIT) { + #ifdef DEBUG_CHUNK_REGION_CREATE + do_rawlog(LT_TRACE, "find_best_region chose to create region %04x", region); + #endif + best_region = create_region(); + } return best_region; } /** Find the best offset in a region to hold a chunk. ! * \param full_len the length of the chunk, including headers. * \param region the region to allocate in. ! * \param old_region the region the chunk was in before (if any). ! * \param old_offset the offset the chunk was at before (if any). */ static u_int_16 ! find_best_offset(u_int_16 full_len, u_int_16 region, ! u_int_16 old_region, u_int_16 old_offset) { ! u_int_16 fits, offset; ! bring_in_region(region); ! fits = 0; ! for (offset = regions[region].in_memory->first_free; offset; ! offset = ChunkNextFree(region, offset)) { ! if (region == old_region) { ! if (offset > old_offset) ! break; ! if (offset + ChunkFullLen(region, offset) == old_offset) ! return fits ? fits : offset; } + if (ChunkFullLen(region, offset) == full_len) + return offset; + if (!fits && ChunkFullLen(region, offset) >= full_len + MIN_REMNANT_LEN) + fits = offset; } ! return fits; } /* ! * Utility Routines - Statistics and debugging */ ! /** Compile a histogram for the region dereferences. ! * \return histogram data for the regions. */ ! static int * ! chunk_regionhist(void) { ! static int histogram[CHUNK_DEREF_MAX + 1]; ! unsigned int j; ! for (j = 0; j <= CHUNK_DEREF_MAX; j++) ! histogram[j] = 0; ! for (j = 0; j < region_count; j++) { ! histogram[RegionDerefs(j)]++; } + return histogram; } ! /** Compile a histogram for the region free space. ! * \return histogram data for the free space. */ ! static int const * ! chunk_freehist(void) { ! static int histogram[CHUNK_DEREF_MAX + 1]; ! unsigned int j; ! for (j = 0; j <= CHUNK_DEREF_MAX; j++) ! histogram[j] = 0; ! for (j = 0; j < region_count; j++) { ! histogram[RegionDerefs(j)] += regions[j].free_bytes; } + return histogram; } ! /** Display statistics to a player, or dump them to a log ! */ ! #define STAT_OUT(x) \ ! do { \ ! s = (x); \ ! if (GoodObject(player)) \ ! notify(player, s); \ ! else \ ! do_rawlog(LT_TRACE, "%s", s); \ ! } while (0) ! ! /** Display the stats summary page. ! * \param player the player to display it to, or NOTHING to log it. */ static void ! chunk_statistics(dbref player) { ! const char *s; ! int overhead; ! int free_count = 0; ! int free_bytes = 0; ! int free_large = 0; ! int used_count = 0; ! int used_bytes = 0; ! u_int_16 rid; ! for (rid = 0; rid < region_count; rid++) { ! free_count += regions[rid].free_count; ! free_bytes += regions[rid].free_bytes; ! free_large += regions[rid].largest_free_chunk; ! used_count += regions[rid].used_count; } ! used_bytes = (REGION_CAPACITY * region_count) - free_bytes; if (!GoodObject(player)) { do_rawlog(LT_TRACE, "---- Chunk statistics"); *************** *** 2177,2187 **** (" %10d free (%10d bytes, %10d (%2d%%) fragmented)", free_count, free_bytes, free_bytes - free_large, free_bytes ? (free_bytes - free_large) * 100 / free_bytes : 0)); - STAT_OUT(tprintf("Regions: %10d total, %8d cached", - region_count, cached_region_count)); overhead = region_count * REGION_SIZE + region_array_len * sizeof(Region); STAT_OUT(tprintf("Storage: %10d total (%2d%% saturation)", overhead, used_bytes * 100 / overhead)); STAT_OUT(tprintf("Paging: %10d out, %10d in", stat_page_out, stat_page_in)); STAT_OUT(" "); --- 1782,1792 ---- (" %10d free (%10d bytes, %10d (%2d%%) fragmented)", free_count, free_bytes, free_bytes - free_large, free_bytes ? (free_bytes - free_large) * 100 / free_bytes : 0)); overhead = region_count * REGION_SIZE + region_array_len * sizeof(Region); STAT_OUT(tprintf("Storage: %10d total (%2d%% saturation)", overhead, used_bytes * 100 / overhead)); + STAT_OUT(tprintf("Regions: %10d total, %8d cached", + region_count, cached_region_count)); STAT_OUT(tprintf("Paging: %10d out, %10d in", stat_page_out, stat_page_in)); STAT_OUT(" "); *************** *** 2190,2201 **** STAT_OUT(tprintf("Activity: %10d creates, %10d deletes this period", stat_create, stat_delete)); STAT_OUT(tprintf("Migration: %10d moves this period", ! stat_migrate_slide + stat_migrate_away + ! stat_migrate_fill_exact + stat_migrate_fill_inexact)); ! STAT_OUT(tprintf(" %10d slide %10d fill exact", ! stat_migrate_slide, stat_migrate_fill_exact)); ! STAT_OUT(tprintf(" %10d away %10d fill inexact", ! stat_migrate_away, stat_migrate_fill_inexact)); } /** Show just the page counts. --- 1795,1806 ---- STAT_OUT(tprintf("Activity: %10d creates, %10d deletes this period", stat_create, stat_delete)); STAT_OUT(tprintf("Migration: %10d moves this period", ! stat_migrate_slide + stat_migrate_move)); ! STAT_OUT(tprintf(" %10d slide %10d move", ! stat_migrate_slide, stat_migrate_move)); ! STAT_OUT(tprintf(" %10d in region%10d out of region", ! stat_migrate_slide + stat_migrate_move - stat_migrate_away, ! stat_migrate_away)); } /** Show just the page counts. *************** *** 2311,2356 **** while (j-- > 1) STAT_OUT(tprintf(" |%s", buffer[j])); STAT_OUT(tprintf(" 0 |%s", buffer[0])); ! STAT_OUT ! (" +----------------------------------------------------------------"); STAT_OUT(tprintf(" 0%31s%32d", "|", 255)); } ! /** Compile a histogram for the region dereferences. ! * \return histogram data for the regions. */ ! static int const * ! chunk_regionhist(void) { ! static int histogram[CHUNK_DEREF_MAX + 1]; ! unsigned int j; ! for (j = 0; j <= CHUNK_DEREF_MAX; j++) ! histogram[j] = 0; ! for (j = 0; j < region_count; j++) { ! histogram[RegionDerefs(j)]++; } - return histogram; } ! /** Compile a histogram for the region free space. ! * \return histogram data for the free space. */ ! static int const * ! chunk_freehist(void) { ! static int histogram[CHUNK_DEREF_MAX + 1]; ! unsigned int j; ! for (j = 0; j <= CHUNK_DEREF_MAX; j++) ! histogram[j] = 0; ! for (j = 0; j < region_count; j++) { ! histogram[RegionDerefs(j)] += regions[j].free_bytes; } ! return histogram; } - #undef STAT_OUT /* --- 1916,2114 ---- while (j-- > 1) STAT_OUT(tprintf(" |%s", buffer[j])); STAT_OUT(tprintf(" 0 |%s", buffer[0])); ! for (j = 0, k = 2; j < 64; j++, k += 4) ! buffer[0][j] = '-'; ! STAT_OUT(tprintf(" +%s", buffer[0])); STAT_OUT(tprintf(" 0%31s%32d", "|", 255)); } ! #undef STAT_OUT ! ! ! /* ! * Utility Routines - Migration */ ! ! static void ! migrate_sort(void) { ! int j, k; ! chunk_reference_t *t; ! for (j = 1; j < m_count; j++) { ! t = m_references[j]; ! for (k = j; k--;) { ! if (m_references[k][0] < t[0]) ! break; ! m_references[k + 1] = m_references[k]; ! } ! m_references[k + 1] = t; } } ! /** Slide an allocated chunk over into a neighboring free space. ! * \param region the region of the free space. ! * \param offset the offset of the free space. ! * \param which the index (in the migration arrays) of the chunk to move. */ ! static void ! migrate_slide(u_int_16 region, u_int_16 offset, int which) { ! Region *rp = regions + region; ! u_int_16 o_len, len, next, other, prev, o_off, o_oth; ! debug_log("migrate_slide %d (%08x) to %04x%04x", ! which, m_references[which][0], region, offset); ! ! bring_in_region(region); ! ! len = ChunkFullLen(region, offset); ! next = ChunkNextFree(region, offset); ! other = ChunkReferenceToOffset(m_references[which][0]); ! o_len = ChunkFullLen(region, other); ! ! o_off = offset; ! o_oth = other; ! if (other > offset) { ! memmove(ChunkPointer(region, offset), ChunkPointer(region, other), o_len); ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: Sliding chunk %08x to %04x%04x", ! m_references[which][0], region, offset); ! #endif ! m_references[which][0] = ChunkReference(region, offset); ! other = offset + o_len; ! } else { ! prev = offset + len - o_len; ! memmove(ChunkPointer(region, prev), ChunkPointer(region, other), o_len); ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: Sliding chunk %08x to %04x%04x", ! m_references[which][0], region, prev); ! #endif ! m_references[which][0] = ChunkReference(region, prev); } ! write_free_chunk(region, other, len, next); ! coalesce_frees(region, other); ! if (rp->in_memory->first_free == offset) { ! rp->in_memory->first_free = other; ! } else { ! prev = rp->in_memory->first_free; ! while (prev && ChunkNextFree(region, prev) != offset) ! prev = ChunkNextFree(region, prev); ! write_next_free(region, prev, other); ! coalesce_frees(region, prev); ! } ! ! stat_migrate_slide++; ! ! #ifdef CHUNK_PARANOID ! if (!region_is_valid(region)) { ! do_rawlog(LT_TRACE, "Invalid region after migrate_slide!"); ! do_rawlog(LT_TRACE, "Was moving %04x%04x to %04x%04x (became %08x)...", ! region, o_oth, region, o_off, m_references[which][0]); ! do_rawlog(LT_TRACE, "Chunk length %04x into hole length %04x", o_len, len); ! debug_dump_region(region, tracelog_fp); ! dump_debug_log(tracelog_fp); ! mush_panic("Invalid region after migrate_slide!"); ! } ! #endif ! } ! ! /** Move an allocated chunk into a free hole. ! * \param region the region of the free space. ! * \param offset the offset of the free space. ! * \param align the alignment to use: 0 = easiest, 1 = left, 2 = right. ! * \param which the index (in the migration arrays) of the chunk to move. ! */ ! static void ! migrate_move(u_int_16 region, u_int_16 offset, int which, int align) ! { ! Region *rp = regions + region; ! u_int_16 s_reg, s_off, s_len, o_off, length; ! Region *srp; ! ! debug_log("migrate_move %d (%08x) to %04x%04x, alignment %d", ! which, m_references[which][0], region, offset, align); ! ! s_reg = ChunkReferenceToRegion(m_references[which][0]); ! s_off = ChunkReferenceToOffset(m_references[which][0]); ! srp = regions + s_reg; ! ! bring_in_region(region); ! if (!srp->in_memory) { ! touch_cache_region(rp->in_memory); ! bring_in_region(s_reg); ! touch_cache_region(rp->in_memory); ! } ! ! s_len = ChunkFullLen(s_reg, s_off); ! length = ChunkFullLen(region, offset); ! ! if (s_reg == region && (s_off + s_len == offset || offset + length == s_off)) { ! migrate_slide(region, offset, which); ! return; ! } ! #ifdef CHUNK_PARANOID ! if (!FitsInSpace(s_len, ChunkFullLen(region, offset))) { ! dump_debug_log(tracelog_fp); ! mush_panicf("Trying to migrate into too small a hole: %04x into %04x!", ! s_len, length); ! } ! #endif ! ! o_off = offset; ! offset = split_hole(region, offset, s_len, align); ! memcpy(ChunkPointer(region, offset), ChunkPointer(s_reg, s_off), s_len); ! #ifdef DEBUG_CHUNK_MIGRATE ! do_rawlog(LT_TRACE, "CHUNK: moving chunk %08x to %04x%04x", ! m_references[which][0], region, offset); ! #endif ! m_references[which][0] = ChunkReference(region, offset); ! rp->total_derefs += ChunkDerefs(region, offset); ! free_chunk(s_reg, s_off); ! ! stat_migrate_move++; ! ! #ifdef CHUNK_PARANOID ! if (!region_is_valid(region)) { ! do_rawlog(LT_TRACE, "Invalid region after migrate_move!"); ! do_rawlog(LT_TRACE, "Was moving %04x%04x to %04x%04x (became %04x%04x)...", ! s_reg, s_off, region, o_off, region, offset); ! do_rawlog(LT_TRACE, "Chunk length %04x into hole length %04x, alignment %d", ! s_len, length, align); ! debug_dump_region(region, tracelog_fp); ! mush_panic("Invalid region after migrate_move!"); ! } ! #endif ! } ! ! static void ! migrate_region(u_int_16 region) ! { ! chunk_reference_t high, low; ! int j, derefs; ! u_int_16 offset, length, best_region, best_offset; ! ! bring_in_region(region); ! ! high = ChunkReference(region, REGION_SIZE); ! low = ChunkReference(region, 0); ! ! for (j = 0; j < m_count; j++) { ! if (low < m_references[j][0] && m_references[j][0] < high) { ! offset = ChunkReferenceToOffset(m_references[j][0]); ! derefs = ChunkDerefs(region, offset); ! length = ChunkFullLen(region, offset); ! best_region = find_best_region(length, derefs, region); ! best_offset = find_best_offset(length, best_region, region, offset); ! if (best_offset) ! migrate_move(best_region, best_offset, j, 1); ! if (best_region != region) ! stat_migrate_away++; ! } ! } ! migrate_sort(); } /* *************** *** 2371,2387 **** mush_panicf("Illegal chunk length requested: %d bytes", len); full_len = LenToFullLen(len); ! region = find_best_region(full_len, derefs, UNHAPPINESS_NEVER); ! if (region >= region_count) ! region = create_region(); ! else ! bring_in_region(region); ! offset = find_best_offset(region, full_len); if (!offset) { region = create_region(); offset = FIRST_CHUNK_OFFSET_IN_REGION; } ! offset = split_hole(region, offset, full_len); write_used_chunk(region, offset, full_len, data, len, derefs); regions[region].total_derefs += derefs; touch_cache_region(regions[region].in_memory); --- 2129,2144 ---- mush_panicf("Illegal chunk length requested: %d bytes", len); full_len = LenToFullLen(len); ! region = find_best_region(full_len, derefs, INVALID_REGION_ID); ! offset = find_best_offset(full_len, region, INVALID_REGION_ID, 0); if (!offset) { region = create_region(); + #ifdef DEBUG_CHUNK_REGION_CREATE + do_rawlog(LT_TRACE, "chunk_create created region %04x", region); + #endif offset = FIRST_CHUNK_OFFSET_IN_REGION; } ! offset = split_hole(region, offset, full_len, 0); write_used_chunk(region, offset, full_len, data, len, derefs); regions[region].total_derefs += derefs; touch_cache_region(regions[region].in_memory); *************** *** 2494,2565 **** void chunk_migration(int count, chunk_reference_t ** references) { ! RegionHeader *rhp; ! int worked, j; unsigned total; /* Before everything, see if we need a new period. */ total = 0; ! for (j = 0; j < (signed) region_count; j++) { ! if (RegionDerefs(j) > (CHUNK_DEREF_MAX / 2)) total++; } ! if (total > cached_region_count || ! total > region_count / 2 + CHUNK_DEREF_MAX / UNHAPPINESS_AWAY_THRESHOLD) chunk_new_period(); ! #ifdef DEBUG_CHUNK_MIGRATE_LOUD ! do_rawlog(LT_TRACE, "CHUNK: Migration started"); ! #endif ! migrate_prep(count, references); ! m_limit = count * 2; ! /* First, try to hardpack stuff */ ! #ifdef DEBUG_CHUNK_MIGRATE_LOUD ! do_rawlog(LT_TRACE, "CHUNK: Migration hardpack"); ! #endif ! do { ! worked = 0; ! for (rhp = cache_head; rhp; rhp = rhp->next) { ! if (rhp->region_id == INVALID_REGION_ID) ! continue; ! while (m_limit && migrate_hardpack(rhp->region_id)) { ! worked = 1; } ! } ! } while (worked); ! ! /* Then, shift crap away */ ! #ifdef DEBUG_CHUNK_MIGRATE_LOUD ! do_rawlog(LT_TRACE, "CHUNK: Migration away"); ! #endif ! for (j = 0; j < count; j++) { ! if (m_limit && migrate_away(j)) { ! m_limit--; ! j = -1; } } - /* Finally, try to softpack stuff */ - #ifdef DEBUG_CHUNK_MIGRATE_LOUD - do_rawlog(LT_TRACE, "CHUNK: Migration softpack"); - #endif - do { - worked = 0; - for (rhp = cache_head; rhp; rhp = rhp->next) { - if (rhp->region_id == INVALID_REGION_ID) - continue; - while (m_limit && migrate_softpack(rhp->region_id)) { - worked = 1; - } - } - } while (worked); - #ifdef DEBUG_CHUNK_MIGRATE_LOUD - do_rawlog(LT_TRACE, "CHUNK: Migration finished"); - #endif m_references = NULL; m_count = 0; } /** Get the number of paged regions. --- 2251,2310 ---- void chunk_migration(int count, chunk_reference_t ** references) { ! int k, l; unsigned total; + u_int_16 region, offset; + + debug_log("*** chunk_migration starts, count = %d", count); /* Before everything, see if we need a new period. */ total = 0; ! for (region = 0; region < region_count; region++) { ! if (RegionDerefs(region) > (CHUNK_DEREF_MAX / 2)) total++; } ! if (total > cached_region_count || total > region_count / 2) chunk_new_period(); ! m_count = count; ! m_references = references; ! migrate_sort(); ! /* Go through each of the regions. */ ! for (region = 0; region < region_count; region++) { ! /* Make sure we have something to migrate, in the region. */ ! for (k = 0; k < m_count; k++) ! if (ChunkReferenceToRegion(m_references[k][0]) == region) ! break; ! if (k >= m_count) ! continue; ! if (!regions[region].in_memory) { ! /* If not in memory, see if we've got an oddball. */ ! while (k < m_count) { ! if (ChunkReferenceToRegion(m_references[k][0]) != region) ! break; ! offset = ChunkReferenceToOffset(m_references[k][0]); ! for (l = 0; l < NUM_ODDBALLS; l++) { ! if (regions[region].oddballs[l] == offset) { ! /* Yup, have an oddball... that's worth bringing it in. */ ! bring_in_region(region); ! goto do_migrate; ! } ! } ! k++; } ! } else { ! do_migrate: ! /* It's in memory, so migrate it. */ ! migrate_region(region); } } m_references = NULL; m_count = 0; + + debug_log("*** chunk_migration ends", count); } /** Get the number of paged regions. *************** *** 2589,2598 **** { /* In any case, this assert should be in main code, not here */ ASSERT(BUFFER_LEN <= MAX_LONG_CHUNK_LEN); - /* Make sure that the away movements can't loop due to the chunks - * being unhappy all by themselves... */ - ASSERT(UNHAPPINESS_AWAY_THRESHOLD > - (REGION_CAPACITY - MIN_REMNANT_LEN) / UNHAPPINESS_SPACE_DIVISOR); #ifdef WIN32 swap_fd = CreateFile(CHUNK_SWAP_FILE, GENERIC_READ | GENERIC_WRITE, --- 2334,2339 ---- *************** *** 2682,2689 **** stat_deref_count = 0; stat_deref_maxxed = 0; stat_migrate_slide = 0; ! stat_migrate_fill_exact = 0; ! stat_migrate_fill_inexact = 0; stat_migrate_away = 0; stat_create = 0; stat_delete = 0; --- 2423,2429 ---- stat_deref_count = 0; stat_deref_maxxed = 0; stat_migrate_slide = 0; ! stat_migrate_move = 0; stat_migrate_away = 0; stat_create = 0; stat_delete = 0;